#5334 合并北京超算云相关代码到V20240129.patch

Merged
chenyifan01 merged 71 commits from fix-5100 into V20240129.patch 1 month ago
  1. +43
    -2
      entity/ai_task.go
  2. +37
    -0
      entity/cluster.go
  3. +7
    -41
      models/action.go
  4. +100
    -81
      models/card_request.go
  5. +30
    -0
      models/cloudbrain.go
  6. +8
    -0
      models/cloudbrain_image.go
  7. +19
    -0
      models/resource_queue.go
  8. +2
    -1
      models/task_config.go
  9. +6
    -0
      modules/auth/wechat/client.go
  10. +81
    -4
      modules/auth/wechat/cloudbrain.go
  11. +1
    -1
      modules/auth/wechat/finetune.go
  12. +1
    -1
      modules/auth/wechat/point.go
  13. +4
    -2
      modules/auth/wechat/template.go
  14. +2
    -0
      modules/notification/base/notifier.go
  15. +5
    -0
      modules/notification/base/null.go
  16. +8
    -0
      modules/notification/notification.go
  17. +7
    -0
      modules/notification/wechat/wechat.go
  18. +10
    -0
      modules/redis/redis_key/wechat_redis_key.go
  19. +19
    -13
      modules/setting/setting.go
  20. +20
    -16
      modules/structs/card_requests.go
  21. +2
    -0
      options/locale/locale_en-US.ini
  22. +2
    -0
      options/locale/locale_zh-CN.ini
  23. +9
    -1
      public/home/home.js
  24. +0
    -91
      routers/admin/cloudbrains.go
  25. +108
    -1
      routers/ai_task/ai_task.go
  26. +4
    -0
      routers/api/v1/api.go
  27. +10
    -0
      routers/api/v1/repo/images.go
  28. +17
    -12
      routers/card_request/card_request.go
  29. +5
    -99
      routers/repo/cloudbrain.go
  30. +28
    -0
      routers/repo/grampus_general.go
  31. +8
    -0
      routers/routes/routes.go
  32. +0
    -108
      routers/user/home.go
  33. +80
    -0
      services/ai_task_service/cluster/c2net.go
  34. +4
    -1
      services/ai_task_service/cluster/cloudbrain_one.go
  35. +4
    -0
      services/ai_task_service/cluster/cloudbrain_two.go
  36. +1
    -0
      services/ai_task_service/cluster/cluster_base.go
  37. +2
    -0
      services/ai_task_service/task/cloudbrain_one_notebook_task.go
  38. +166
    -0
      services/ai_task_service/task/grampus_general_task.go
  39. +45
    -3
      services/ai_task_service/task/task_base.go
  40. +155
    -2
      services/ai_task_service/task/task_list.go
  41. +46
    -21
      services/ai_task_service/task/task_service.go
  42. +41
    -29
      services/card_request/card_request.go
  43. +3
    -1
      services/cloudbrain/resource/resource_queue.go
  44. +1
    -1
      services/socketwrap/clientManager.go
  45. +4
    -573
      templates/admin/cloudbrain/list.tmpl
  46. +1
    -0
      templates/repo/cloudbrain/benchmark/index.tmpl
  47. +8
    -0
      templates/repo/grampus/general/list.tmpl
  48. +1
    -0
      templates/repo/grampus/general/new.tmpl
  49. +1
    -0
      templates/repo/grampus/general/show.tmpl
  50. +3
    -525
      templates/user/dashboard/cloudbrains.tmpl
  51. +10
    -2
      templates/user/dashboard/feeds.tmpl
  52. +20
    -0
      web_src/vuepages/apis/modules/cloudbrain.js
  53. +28
    -0
      web_src/vuepages/apis/modules/common.js
  54. +1
    -0
      web_src/vuepages/apis/modules/images.js
  55. +86
    -0
      web_src/vuepages/components/cloudbrain/GeneralTaskCodeTips.vue
  56. +24
    -3
      web_src/vuepages/components/cloudbrain/ImageSelectV1.vue
  57. +1
    -1
      web_src/vuepages/components/cloudbrain/ImageSelectV2.vue
  58. +14
    -1
      web_src/vuepages/components/cloudbrain/SDKCode.vue
  59. +12
    -4
      web_src/vuepages/components/cloudbrain/details/ConfigInfo.vue
  60. +1
    -0
      web_src/vuepages/components/cloudbrain/details/OperationProfile.vue
  61. +2
    -1
      web_src/vuepages/const/index.js
  62. +20
    -1
      web_src/vuepages/langs/config/en-US.js
  63. +19
    -0
      web_src/vuepages/langs/config/zh-CN.js
  64. +77
    -2
      web_src/vuepages/pages/cloudbrain/configs.js
  65. +15
    -9
      web_src/vuepages/pages/cloudbrain/create/index.vue
  66. +4
    -2
      web_src/vuepages/pages/cloudbrain/list/index.vue
  67. +733
    -0
      web_src/vuepages/pages/cloudbrain/tasks/index.vue
  68. +17
    -0
      web_src/vuepages/pages/cloudbrain/tasks/vp-cloudbrain-tasks.js
  69. +12
    -3
      web_src/vuepages/pages/cloudbrain/tools.js
  70. +36
    -1
      web_src/vuepages/pages/computingpower/components/DemandForm.vue
  71. +14
    -46
      web_src/vuepages/pages/modelmanage/intro/index.vue
  72. +3
    -0
      web_src/vuepages/pages/reward/point/utils.js

+ 43
- 2
entity/ai_task.go View File

@@ -74,6 +74,7 @@ type QueryAITaskRes struct {
EarlyVersionList []*AITaskDetailInfo `json:"early_version_list"`
CanCreateVersion bool `json:"can_create_version"`
CanDownload bool `json:"can_download"`
CanModify bool `json:"can_modify"`
}

func (r *QueryAITaskRes) TryToRemoveDatasetAndModelInfo(currentUser *models.User) {
@@ -211,18 +212,25 @@ type AITaskBriefInfo struct {
IsFileNotebook bool `json:"is_file_notebook"`
IsFineTuneTask bool `json:"is_fine_tune_task"`
APPName string `json:"app_name"`
AccCardType string `json:"acc_card_type"`
JobName string `json:"job_name"`
}

func (a *AITaskBriefInfo) Tr(language string) {
aiCenterInfo := strings.Split(a.AICenter, "+")
aiCenterCode := aiCenterInfo[0]
aiCenterName := ""
if len(aiCenterCode) >= 2 {
if len(aiCenterInfo) >= 2 {
aiCenterName = aiCenterInfo[1]
}
a.AICenter = models.GetAiCenterShow(aiCenterCode, aiCenterName, language)
}

func (a *AITaskBriefInfo) ClearNonPublicFields() *AITaskBriefInfo {
a.JobName = ""
return a
}

type AITaskListRes struct {
Tasks []*AITaskInfo4List `json:"tasks"`
Total int64 `json:"total"`
@@ -236,9 +244,25 @@ type AITaskInfo4List struct {
Creator UserBriefInfo `json:"creator"`
CanModify bool `json:"can_modify"`
CanDelete bool `json:"can_delete"`
RepoName string `json:"repo_name"`
OwnerName string `json:"owner_name"`
}

func ConvertCloudbrainToAITaskBriefInfo(task *models.Cloudbrain) *AITaskBriefInfo {
accCardType := ""
if task.Spec != nil {
accCardType = task.Spec.AccCardType
}
aiCenter := task.AiCenter
switch task.Type {
case models.TypeCloudBrainOne:
aiCenter = models.AICenterOfCloudBrainOne
case models.TypeCloudBrainTwo:
aiCenter = models.AICenterOfCloudBrainTwo
case models.TypeCDCenter:
aiCenter = models.AICenterOfChengdu
}

return &AITaskBriefInfo{
ID: task.ID,
JobType: task.JobType,
@@ -251,10 +275,12 @@ func ConvertCloudbrainToAITaskBriefInfo(task *models.Cloudbrain) *AITaskBriefInf
ComputeSource: task.GetStandardComputeSource(),
StartTime: task.StartTime,
EndTime: task.EndTime,
AICenter: task.AiCenter,
AICenter: aiCenter,
IsFileNotebook: task.IsFileNoteBookTask(),
IsFineTuneTask: task.FineTune,
APPName: task.AppName,
AccCardType: accCardType,
JobName: task.JobName,
}
}

@@ -328,6 +354,18 @@ type GetTaskListReq struct {
Operator *models.User
IsRepoOwner bool
}
type GetMyTaskListReq struct {
models.ListOptions
ComputeSource *models.ComputeSource
JobType string
JobStatus string
User *models.User
IsRepoOwner bool
Keyword string
AICenter string
Cluster string
ExcludeStatus []string
}

type AITaskBaseConfig struct {
ContainerSteps map[ContainerDataType]*ContainerBuildOpts `json:"container_configs"`
@@ -337,6 +375,9 @@ type AITaskBaseConfig struct {
DatasetsMaxNum int
ModelMaxNum int
ModelLimitSizeGB int
AutoStopDuration int64
//主动检测调式地址是否可用
DebugAddressCheck bool
}

func GetAITaskConfigFromCloudbrainConfig(config *models.CloudbrainConfig) *AITaskBaseConfig {


+ 37
- 0
entity/cluster.go View File

@@ -50,6 +50,43 @@ type CreateNoteBookTaskResponse struct {
Status string
UserID string
}
type CreateGeneralTaskRequest struct {
Name string
Description string
Tasks []GeneralTask
PrimitiveDatasetName string
RepoName string
}

type GeneralTask struct {
AutoStopDuration int64
Name string
Capacity int
Queues []models.ResourceQueue
Code []ContainerData
Datasets []ContainerData
PreTrainModel []ContainerData
OutPut []ContainerData
ImageId string
ImageUrl string
ResourceSpecId string
BootFile string
Spec *models.Specification
EnvVariables models.GrampusEnvVarReq
}

type CreateGeneralTaskResponse struct {
StartedAt int64
RunSec int64
CompletedAt int64
CreatedAt int64
UpdatedAt int64
Desc string
JobID string
Name string
Status string
UserID string
}

type RestartNoteBookTaskResponse struct {
JobId string `json:"newId"`


+ 7
- 41
models/action.go View File

@@ -79,7 +79,8 @@ const (
ActionCreateGrampusGPUInferenceTask //50
ActionCreateGrampusILUVATARInferenceTask //51
ActionInviteFriendRegister //52
ActionCreateGrampusILUVATARTrainTask
ActionCreateGrampusILUVATARTrainTask //53
ActionCreateGeneralGPUTask //54
)

// Action represents user operation type and other information to
@@ -114,15 +115,6 @@ type ActionShow struct {
IssueInfos []string
CommentLink string
Cloudbrain *CloudbrainShow4Action
Data map[string]interface{}
}

func (a *ActionShow) AddData(key string, val interface{}) {
if a.Data == nil {
a.Data = map[string]interface{}{key: val}
} else {
a.Data[key] = val
}
}

// GetOpType gets the ActionType of this action.
@@ -153,6 +145,8 @@ func (a *Action) FilterCloudbrainInfo() {
if a.Cloudbrain.DeletedAt.IsZero() {
newCloudbrain := &Cloudbrain{}
newCloudbrain.ID = a.Cloudbrain.ID
newCloudbrain.JobType = a.Cloudbrain.JobType
newCloudbrain.ComputeResource = a.Cloudbrain.ComputeResource
a.Cloudbrain = newCloudbrain
} else {
a.Cloudbrain = nil
@@ -304,28 +298,7 @@ func (a *Action) ToShow() *ActionShow {
if strings.Contains(a.Content, "|") && a.IsIssueAction() {
actionShow.IssueInfos = a.GetIssueInfos()
}
if strings.Contains(a.Content, "|") && a.IsInviteAction() {
ids := strings.Split(a.Content, "|")
if len(ids) >= 2 {
var invitedId int64
var invitedName string
if len(ids) >= 4 {
invitedName = ids[3]
}
invitedId, _ = strconv.ParseInt(ids[1], 10, 64)
if invitedId > 0 {
invitedUser, _ := GetUserByID(invitedId)
if invitedUser != nil {
actionShow.AddData("InvitedUserName", invitedUser.Name)
actionShow.AddData("InvitedUserNotExists", false)
} else {
actionShow.AddData("InvitedUserName", invitedName)
actionShow.AddData("InvitedUserNotExists", true)
}
}
}
actionShow.IssueInfos = a.GetIssueInfos()
}

if a.Repo != nil {
actionShow.RepoLink = a.GetRepoLink()
actionShow.ShortRepoFullDisplayName = a.ShortRepoFullDisplayName()
@@ -468,7 +441,8 @@ func (a *Action) IsCloudbrainAction() bool {
ActionCreateSuperComputeTask,
ActionCreateGrampusILUVATARInferenceTask,
ActionCreateGrampusGPUInferenceTask,
ActionCreateGrampusILUVATARTrainTask:
ActionCreateGrampusILUVATARTrainTask,
ActionCreateGeneralGPUTask:
return true
}
return false
@@ -492,14 +466,6 @@ func (a *Action) IsIssueAction() bool {
return false
}

func (a *Action) IsInviteAction() bool {
switch a.OpType {
case ActionInviteFriendRegister:
return true
}
return false
}

// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
RequestedUser *User // the user we want activity for


+ 100
- 81
models/card_request.go View File

@@ -19,56 +19,64 @@ const OrderByIDDesc = "card_request.id desc"
const OrderByStatus = "card_request.status asc,card_request.id desc"

type CardRequest struct {
ID int64 `xorm:"pk autoincr"`
ComputeResource string
UID int64
UserName string `xorm:"-"`
CardType string
AccCardsNum string
DiskCapacity int64
ResourceType int
BeginDate string
BeginUnix int64 `xorm:"INDEX"`
EndDate string
EndUnix int64 `xorm:"INDEX"`
Contact string
PhoneNumber string
EmailAddress string
Wechat string
Org string `xorm:"varchar(500)"`
Description string `xorm:"varchar(3000)"`
Status int
Review string `xorm:"varchar(3000)"`
CreatedUnix int64 `xorm:"INDEX created"`
UpdatedUnix int64 `xorm:"INDEX updated"`
DeleteUnix int64 `xorm:"deleted"`
ID int64 `xorm:"pk autoincr"`
ComputeResource string
UID int64
UserName string `xorm:"-"`
CardType string
AccCardsNum string
DiskCapacity int64
ResourceType int
BeginDate string
BeginUnix int64 `xorm:"INDEX"`
EndDate string
EndUnix int64 `xorm:"INDEX"`
Contact string
PhoneNumber string
EmailAddress string
Wechat string
IsResearchProject bool
InstitutionName string
ProjectName string
ProjectCode string
Org string `xorm:"varchar(500)"`
Description string `xorm:"varchar(3000)"`
Status int
Review string `xorm:"varchar(3000)"`
CreatedUnix int64 `xorm:"INDEX created"`
UpdatedUnix int64 `xorm:"INDEX updated"`
DeleteUnix int64 `xorm:"deleted"`
}

type CardRequestSpecRes struct {
ID int64
ComputeResource string
UID int64
UserName string
CardType string
AccCardsNum string
DiskCapacity int64
ResourceType int
BeginDate string
BeginUnix int64
EndDate string
EndUnix int64
Contact string
PhoneNumber string
EmailAddress string
Wechat string
Org string
Description string
Status int
Review string
CreatedUnix int64
UpdatedUnix int64
DeleteUnix int64
Specs []RequestSpecInfo
ID int64
ComputeResource string
UID int64
UserName string
CardType string
AccCardsNum string
DiskCapacity int64
ResourceType int
BeginDate string
BeginUnix int64
EndDate string
EndUnix int64
Contact string
PhoneNumber string
EmailAddress string
Wechat string
IsResearchProject bool
InstitutionName string
ProjectName string
ProjectCode string
Org string
Description string
Status int
Review string
CreatedUnix int64
UpdatedUnix int64
DeleteUnix int64
Specs []RequestSpecInfo
}

func (CardRequestSpecRes) TableName() string {
@@ -88,17 +96,18 @@ type CardRequestOptions struct {
OrderBy string
Keyword string

AiCenterCode string
QueueId int64
ComputeResource string
AccCardType string
Cluster string
ResourceType int
UseBeginTime int64
UseEndTime int64
BeginTimeUnix int64
EndTimeUnix int64
NeedSpec bool
AiCenterCode string
QueueId int64
ComputeResource string
AccCardType string
Cluster string
ResourceType int
UseBeginTime int64
UseEndTime int64
BeginTimeUnix int64
EndTimeUnix int64
NeedSpec bool
IsResearchProject int
}
type CardRequestShowList struct {
Total int64 `json:"total"`
@@ -106,27 +115,31 @@ type CardRequestShowList struct {
}

type CardRequestSpecShow struct {
ID int64 `json:"id"`
ComputeResource string `json:"compute_resource"`
CardType string `json:"card_type"`
AccCardsNum string `json:"acc_cards_num"`
BeginDate string `json:"begin_date"`
EndDate string `json:"end_date"`
ResourceType int `json:"resource_type"`
DiskCapacity int64 `json:"disk_capacity"`
UID int64 `json:"uid"`
UserName string `json:"user_name"`
TargetCenter []string `json:"target_center"`
Contact string `json:"contact"`
PhoneNumber string `json:"phone_number"`
EmailAddress string `json:"email_address"`
Wechat string `json:"wechat"`
Org string `json:"org"`
Description string `json:"description"`
Status int `json:"status"`
Review string `json:"review"`
CreatedUnix int64 `json:"created_unix"`
Specs []RequestSpecInfo `json:"specs"`
ID int64 `json:"id"`
ComputeResource string `json:"compute_resource"`
CardType string `json:"card_type"`
AccCardsNum string `json:"acc_cards_num"`
BeginDate string `json:"begin_date"`
EndDate string `json:"end_date"`
ResourceType int `json:"resource_type"`
DiskCapacity int64 `json:"disk_capacity"`
UID int64 `json:"uid"`
UserName string `json:"user_name"`
TargetCenter []string `json:"target_center"`
Contact string `json:"contact"`
PhoneNumber string `json:"phone_number"`
EmailAddress string `json:"email_address"`
Wechat string `json:"wechat"`
IsResearchProject bool `json:"is_research_project"`
InstitutionName string `json:"institution_name"`
ProjectName string `json:"project_name"`
ProjectCode string `json:"project_code"`
Org string `json:"org"`
Description string `json:"description"`
Status int `json:"status"`
Review string `json:"review"`
CreatedUnix int64 `json:"created_unix"`
Specs []RequestSpecInfo `json:"specs"`
}

type RequestSpecInfo struct {
@@ -330,11 +343,17 @@ func SearchCardRequest(opts *CardRequestOptions) (int64, []*CardRequestSpecRes,
if opts.OrderBy == "" {
opts.OrderBy = OrderByIDDesc
}
if opts.IsResearchProject == 1 {
cond = cond.And(builder.Or(builder.Eq{"card_request.is_research_project": false}, builder.IsNull{"card_request.is_research_project"}))
}
if opts.IsResearchProject == 2 {
cond = cond.And(builder.Eq{"card_request.is_research_project": true})
}

cond = cond.And(builder.NewCond().Or(builder.Eq{"card_request.delete_unix": 0}).Or(builder.IsNull{"card_request.delete_unix"}))
cols := []string{"card_request.id", "card_request.compute_resource", "card_request.contact", "card_request.card_type", "card_request.acc_cards_num",
"card_request.disk_capacity", "card_request.resource_type", "card_request.begin_date", "card_request.end_date", "card_request.uid",
"card_request.phone_number", "card_request.email_address", "card_request.wechat", "card_request.org", "card_request.description", "card_request.status", "card_request.review",
"card_request.phone_number", "card_request.email_address", "card_request.wechat", "card_request.is_research_project", "card_request.institution_name", "card_request.project_name", "card_request.project_code", "card_request.org", "card_request.description", "card_request.status", "card_request.review",
"card_request.created_unix"}
var count int64
var err error
@@ -440,6 +459,6 @@ func SearchCardRequest(opts *CardRequestOptions) (int64, []*CardRequestSpecRes,
}

func UpdateCardRequest(cardRequest *CardRequest) error {
_, err := x.ID(cardRequest.ID).Cols("compute_resource", "contact", "card_type", "acc_cards_num", "disk_capacity", "resource_type", "begin_date", "end_date", "phone_number", "wechat", "email_address", "org", "description", "begin_unix", "end_unix").Update(cardRequest)
_, err := x.ID(cardRequest.ID).Cols("compute_resource", "contact", "card_type", "acc_cards_num", "disk_capacity", "resource_type", "begin_date", "end_date", "phone_number", "wechat", "is_research_project", "institution_name", "project_name", "project_code", "email_address", "org", "description", "begin_unix", "end_unix").Update(cardRequest)
return err
}

+ 30
- 0
models/cloudbrain.go View File

@@ -80,6 +80,7 @@ const (
JobTypeInference JobType = "INFERENCE"
JobTypeOnlineInference JobType = "ONLINEINFERENCE"
JobTypeSuperCompute JobType = "HPC"
JobTypeGeneral JobType = "GENERAL"

//notebook
ModelArtsCreateQueue ModelArtsJobStatus = "CREATE_QUEUING" //免费资源创建排队中
@@ -396,6 +397,19 @@ func (task *Cloudbrain) ToShow() *CloudbrainShow {
return c
}

func (task *Cloudbrain) LoadSpec() error {
if task.Spec != nil {
return nil
}
spec := &CloudbrainSpec{}
_, err := x.Where("cloudbrain_id = ?", task.ID).Get(spec)
if err != nil {
return err
}
task.Spec = spec.ConvertToSpecification()
return nil
}

func (task *Cloudbrain) IsRestartTask() bool {
n, _ := x.Where("display_job_name = ?", task.DisplayJobName).Unscoped().Count(&Cloudbrain{})
if n > 1 {
@@ -487,6 +501,22 @@ func (task *Cloudbrain) GetAiCenter() string {

}

func (task *Cloudbrain) GetAiCenterName() string {
if task.Type == TypeCloudBrainOne {
return "云脑一"
} else if task.Type == TypeCloudBrainTwo {
return "云脑二"
} else if task.Type == TypeCDCenter {
return "启智成都智算"
} else {
tmpArray := strings.Split(task.AiCenter, "+")
if len(tmpArray) == 2 {
return tmpArray[1]
}
}
return task.AiCenter
}

// 是否为在线notebook文件任务
func (task *Cloudbrain) IsFileNoteBookTask() bool {
return task.JobType == string(JobTypeDebug) && task.BootFile != ""


+ 8
- 0
models/cloudbrain_image.go View File

@@ -117,6 +117,7 @@ type SearchAvailableValueOptions struct {
IncludeOwnerOnly bool
IncludeStarByMe bool
UID int64
AiCenterId string
}

type SearchImageOptions struct {
@@ -142,6 +143,7 @@ type SearchImageOptions struct {
AiCenterId string
TrainType string
ComputeResource string
OnlyOpenIImage bool
ListOptions
SearchOrderBy
}
@@ -502,6 +504,9 @@ func GetImageAvailableColumnValues(opts *SearchAvailableValueOptions) []string {

cond = cond.And(builder.Eq{"uid": opts.UID})
}
if opts.AiCenterId != "" {
cond = cond.And(builder.Like{"ai_center_images", "\"aiCenterId\":\"" + opts.AiCenterId + "\""})
}

if opts.IncludeStarByMe {

@@ -661,6 +666,9 @@ func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
if opts.CloudbrainType >= 0 {
cond = cond.And(builder.Eq{"cloudbrain_type": opts.CloudbrainType})
}
if opts.OnlyOpenIImage {
cond = cond.And(builder.Or(builder.Eq{"grampus_base_image": 0}, builder.IsNull{"grampus_base_image"}))
}

return cond
}


+ 19
- 0
models/resource_queue.go View File

@@ -535,6 +535,25 @@ func GetAiCenterShow(aiCenterCode, aiCenterName, language string) string {
if aiCenterName == "" {
aiCenterName = aiCenterCode
}
if aiCenterCode == AICenterOfCloudBrainOne {
if language == defaultLanguage {
return "云脑一"
} else {
return AICenterOfCloudBrainOne
}
} else if aiCenterCode == AICenterOfCloudBrainTwo {
if language == defaultLanguage {
return "云脑二"
} else {
return AICenterOfCloudBrainTwo
}
} else if aiCenterCode == AICenterOfChengdu {
if language == defaultLanguage {
return "启智成都智算"
} else {
return AICenterOfChengdu
}
}
if setting.AiCenterCodeAndNameAndLocMapInfo != nil {
if info, ok := setting.AiCenterCodeAndNameAndLocMapInfo[aiCenterCode]; ok {
if language == defaultLanguage {


+ 2
- 1
models/task_config.go View File

@@ -86,7 +86,8 @@ func GetTaskTypeFromAction(a ActionType) TaskType {
ActionCreateGrampusGPUTrainTask,
ActionCreateGrampusGPUInferenceTask,
ActionCreateGrampusILUVATARInferenceTask,
ActionCreateGrampusILUVATARTrainTask:
ActionCreateGrampusILUVATARTrainTask,
ActionCreateGeneralGPUTask:
return TaskCreateCloudbrainTask
case ActionCreateRepo:
return TaskCreatePublicRepo


+ 6
- 0
modules/auth/wechat/client.go View File

@@ -70,6 +70,12 @@ type DefaultWechatTemplate struct {
Remark TemplateValue `json:"remark"`
}

type WechatDurationWechatTemplate struct {
Keyword1 TemplateValue `json:"thing11"`
Keyword2 TemplateValue `json:"time9"`
Keyword3 TemplateValue `json:"thing3"`
}

type ActionInfo struct {
Scene Scene `json:"scene"`
}


+ 81
- 4
modules/auth/wechat/cloudbrain.go View File

@@ -12,14 +12,15 @@ import (
type JobOperateType string

const (
JobOperateTypeStart JobOperateType = "start"
JobOperateTypeStop JobOperateType = "stop"
JobOperateTypeStart JobOperateType = "start"
JobOperateTypeStop JobOperateType = "stop"
JobOperateTypeLongRunning JobOperateType = "long_running"
)

type CloudbrainStartMsg struct {
}

func (CloudbrainStartMsg) Data(ctx *TemplateContext) *DefaultWechatTemplate {
func (CloudbrainStartMsg) Data(ctx *TemplateContext) interface{} {
return &DefaultWechatTemplate{
First: TemplateValue{Value: setting.CloudbrainStartedTitle},
Keyword1: TemplateValue{Value: ctx.Cloudbrain.DisplayJobName},
@@ -61,7 +62,7 @@ func (CloudbrainStartMsg) TemplateId(ctx *TemplateContext) string {
type CloudbrainStopMsg struct {
}

func (CloudbrainStopMsg) Data(ctx *TemplateContext) *DefaultWechatTemplate {
func (CloudbrainStopMsg) Data(ctx *TemplateContext) interface{} {
return &DefaultWechatTemplate{
First: TemplateValue{Value: fmt.Sprintf(setting.CloudbrainStoppedTitle, ctx.Cloudbrain.Status)},
Keyword1: TemplateValue{Value: ctx.Cloudbrain.DisplayJobName},
@@ -107,6 +108,7 @@ func (CloudbrainStopMsg) TemplateId(ctx *TemplateContext) string {
var startMsg = &CloudbrainStartMsg{}
var stopMsg = &CloudbrainStopMsg{}
var ComingStopMsg = &CloudbrainComingToStopMsg{}
var LongRunningMsg = &CloudbrainLongRunningMsg{}

func GetTemplateFromOperateType(operate JobOperateType) Template {
switch operate {
@@ -150,6 +152,14 @@ func getCloudbrainTemplateUrl(cloudbrain models.Cloudbrain, repo *models.Reposit
}
case string(models.JobTypeInference):
url += "/modelarts/inference-job/" + fmt.Sprint(cloudbrain.JobID)
case string(models.JobTypeGeneral):
if cloudbrain.Type == models.TypeCloudBrainOne {
url += "/cloudbrain/general/" + fmt.Sprint(cloudbrain.ID)
} else if cloudbrain.Type == models.TypeCloudBrainTwo {
url += "/modelarts/general/" + fmt.Sprint(cloudbrain.ID)
} else if cloudbrain.Type == models.TypeC2Net {
url += "/grampus/general/" + fmt.Sprint(cloudbrain.ID)
}
}
return url
}
@@ -170,6 +180,73 @@ func getJobTypeDisplayName(jobType string) string {
string(models.JobTypeBrainScore),
string(models.JobTypeSnn4Ecoset):
return "推理任务"
case string(models.JobTypeGeneral):
return "通用任务"
}
return ""
}

type CloudbrainLongRunningMsg struct {
}

func (CloudbrainLongRunningMsg) Data(ctx *TemplateContext) interface{} {
formattedDuration := formatDuration(ctx.Duration)
formattedRemark := ""
if formattedDuration == "" {
formattedRemark = "任务运行中"
} else {
formattedRemark = fmt.Sprintf("任务运行时间超过%s", formattedDuration)
}
d := int64(ctx.Duration.Seconds())
t := int64(ctx.Cloudbrain.StartTime) + d
return &WechatDurationWechatTemplate{
Keyword1: TemplateValue{Value: ctx.Cloudbrain.DisplayJobName},
Keyword2: TemplateValue{Value: time.Unix(t, 0).Format("2006-01-02 15:04:05")},
Keyword3: TemplateValue{Value: formattedRemark},
}
}

func formatDuration(duration time.Duration) string {
switch {
case duration >= time.Second && duration < time.Minute:
return fmt.Sprintf("%d秒", int(duration.Seconds()))
case duration >= time.Minute && duration < time.Hour:
return fmt.Sprintf("%d分钟", int(duration.Minutes()))
case duration >= time.Hour && duration < 24*time.Hour:
return fmt.Sprintf("%d小时", int(duration.Hours()))
case duration >= 24*time.Hour:
days := duration / (24 * time.Hour)
return fmt.Sprintf("%d天", days)
default:
return ""
}
}

func (CloudbrainLongRunningMsg) ShouldSend(ctx *TemplateContext) bool {
if len(setting.CloudbrainLongRunningNotifyList) == 0 {
return false
}
for _, v := range setting.CloudbrainStartedNotifyList {
if v == ctx.Cloudbrain.JobType {
return true
}
}
return false
}

func (CloudbrainLongRunningMsg) MsgId(ctx *TemplateContext) string {
return string(JobOperateTypeLongRunning) + "_" + fmt.Sprint(ctx.Cloudbrain.ID) + "_" + ctx.Duration.String()
}

func (CloudbrainLongRunningMsg) Url(ctx *TemplateContext) string {
repo, err := models.GetRepositoryByID(ctx.Cloudbrain.RepoID)
if err != nil {
log.Error("CloudbrainStartMsg GetRepositoryByID error,%v", err)
return ""
}
return getCloudbrainTemplateUrl(*ctx.Cloudbrain, repo)
}

func (CloudbrainLongRunningMsg) TemplateId(ctx *TemplateContext) string {
return setting.CloudbrainLongRunningTemplateId
}

+ 1
- 1
modules/auth/wechat/finetune.go View File

@@ -12,7 +12,7 @@ type FinetuneStartMsg struct {

var FinetuneMsg = &FinetuneStartMsg{}

func (FinetuneStartMsg) Data(ctx *TemplateContext) *DefaultWechatTemplate {
func (FinetuneStartMsg) Data(ctx *TemplateContext) interface{} {
return &DefaultWechatTemplate{
First: TemplateValue{Value: setting.FineTune.Pangu.Wechat.Title},
Keyword1: TemplateValue{Value: ctx.ModelartsDeploy.DisplayJobName},


+ 1
- 1
modules/auth/wechat/point.go View File

@@ -11,7 +11,7 @@ import (
type CloudbrainComingToStopMsg struct {
}

func (CloudbrainComingToStopMsg) Data(ctx *TemplateContext) *DefaultWechatTemplate {
func (CloudbrainComingToStopMsg) Data(ctx *TemplateContext) interface{} {
var balance int64
if ctx.PointAccount != nil {
balance = ctx.PointAccount.Balance


+ 4
- 2
modules/auth/wechat/template.go View File

@@ -3,6 +3,7 @@ package wechat
import (
"errors"
"fmt"
"time"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
@@ -11,7 +12,7 @@ import (

type Template interface {
ShouldSend(ctx *TemplateContext) bool
Data(ctx *TemplateContext) *DefaultWechatTemplate
Data(ctx *TemplateContext) interface{}
MsgId(ctx *TemplateContext) string
Url(ctx *TemplateContext) string
TemplateId(ctx *TemplateContext) string
@@ -22,6 +23,7 @@ type TemplateContext struct {
EstimatedEndTime timeutil.TimeStamp
PointAccount *models.PointAccount
ModelartsDeploy *models.ModelartsDeploy
Duration time.Duration
}

func SendTemplateMsg(template Template, ctx *TemplateContext, userId int64) error {
@@ -49,7 +51,7 @@ func SendTemplateMsg(template Template, ctx *TemplateContext, userId int64) erro
ClientMsgId: template.MsgId(ctx),
Data: d,
}
log.Info("SendTemplateMsg before calling.userId = %d,req=%v data=%v", userId, req, *d)
log.Info("SendTemplateMsg before calling.userId = %d,req=%v data=%v", userId, req, d)
err, retryFlag := sendTemplateMsg(req)
if retryFlag {
log.Info("SendTemplateMsg calling.userId = %d", userId)


+ 2
- 0
modules/notification/base/notifier.go View File

@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/timeutil"
"time"
)

// Notifier defines an interface to notify receiver
@@ -68,4 +69,5 @@ type Notifier interface {
NotifyCloudbrainTaskComingToFinished(cloudbrain *models.Cloudbrain, endTime timeutil.TimeStamp, account *models.PointAccount)
NotifyChangeFinetuneStatus(deployment *models.ModelartsDeploy)
NotifyInviteFriendRegister(inviter, invited *models.User)
NotifyLongRunningAITask(cloudbrain *models.Cloudbrain, duration time.Duration)
}

+ 5
- 0
modules/notification/base/null.go View File

@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/timeutil"
"time"
)

// NullNotifier implements a blank notifier
@@ -193,3 +194,7 @@ func (*NullNotifier) NotifyChangeFinetuneStatus(deployment *models.ModelartsDepl
func (*NullNotifier) NotifyInviteFriendRegister(inviter, invited *models.User) {

}

func (*NullNotifier) NotifyLongRunningAITask(cloudbrain *models.Cloudbrain, duration time.Duration) {

}

+ 8
- 0
modules/notification/notification.go View File

@@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"time"
)

var (
@@ -340,3 +341,10 @@ func NotifyInviteFriendRegister(inviter, invited *models.User) {
notifier.NotifyInviteFriendRegister(inviter, invited)
}
}

// NotifyAITaskDuration
func NotifyLongRunningAITask(cloudbrain *models.Cloudbrain, duration time.Duration) {
for _, notifier := range notifiers {
notifier.NotifyLongRunningAITask(cloudbrain, duration)
}
}

+ 7
- 0
modules/notification/wechat/wechat.go View File

@@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification/base"
"code.gitea.io/gitea/modules/timeutil"
"time"
)

type wechatNotifier struct {
@@ -47,3 +48,9 @@ func (*wechatNotifier) NotifyChangeFinetuneStatus(deployment *models.ModelartsDe
template := wechat.FinetuneMsg
go wechat.SendTemplateMsg(template, &wechat.TemplateContext{ModelartsDeploy: deployment}, deployment.UserID)
}

func (*wechatNotifier) NotifyLongRunningAITask(cloudbrain *models.Cloudbrain, duration time.Duration) {
log.Info("NotifyAITaskDuration cloudbrain.id=%d", cloudbrain.ID)
template := wechat.LongRunningMsg
go wechat.SendTemplateMsg(template, &wechat.TemplateContext{Cloudbrain: cloudbrain, Duration: duration}, cloudbrain.UserID)
}

+ 10
- 0
modules/redis/redis_key/wechat_redis_key.go View File

@@ -1,5 +1,11 @@
package redis_key

import (
"code.gitea.io/gitea/models"
"fmt"
"time"
)

const PREFIX = "wechat"

func WechatBindingUserIdKey(sceneStr string) string {
@@ -12,3 +18,7 @@ func WechatAccessTokenKey() string {
func AccessTokenLockKey() string {
return KeyJoin(PREFIX, "access_token_lock")
}

func LongRunningNotificationKey(cloudbrain *models.Cloudbrain, duration time.Duration) string {
return KeyJoin(PREFIX, fmt.Sprint(cloudbrain.ID), duration.String(), "long_running_notification")
}

+ 19
- 13
modules/setting/setting.go View File

@@ -716,19 +716,22 @@ var (
TreePathOfSubscribe string

//wechat template msg config
CloudbrainStartedTemplateId string
CloudbrainStartedNotifyList []string
CloudbrainStartedTitle string
CloudbrainStartedRemark string
CloudbrainStoppedTemplateId string
CloudbrainStoppedNotifyList []string
CloudbrainStoppedTitle string
CloudbrainStoppedRemark string
CloudbrainComingStopTemplateId string
CloudbrainComingStopTitle string
CloudbrainComingStopChargeLink string
CloudbrainComingStopRemark string
CloudbrainComingStopSendFlag bool
CloudbrainStartedTemplateId string
CloudbrainStartedNotifyList []string
CloudbrainStartedTitle string
CloudbrainStartedRemark string
CloudbrainStoppedTemplateId string
CloudbrainStoppedNotifyList []string
CloudbrainStoppedTitle string
CloudbrainStoppedRemark string
CloudbrainComingStopTemplateId string
CloudbrainComingStopTitle string
CloudbrainComingStopChargeLink string
CloudbrainComingStopRemark string
CloudbrainComingStopSendFlag bool
CloudbrainLongRunningNotifyList []string
CloudbrainLongRunningTemplateId string
CloudbrainLongRunningNotifyInterval time.Duration

//repo square config
IncubationSourceOrgName string
@@ -1717,6 +1720,9 @@ func NewContext() {
CloudbrainComingStopChargeLink = sec.Key("CLOUDBRAIN_COMING_STOP_CHARGE_LINK").MustString("请参考“个人中心 > 算力积分”页面的“积分获取说明”获取积分。")
CloudbrainComingStopRemark = sec.Key("CLOUDBRAIN_COMING_STOP_REMARK").MustString("为了不影响您使用,请您尽快获取算力积分。")
CloudbrainComingStopSendFlag = sec.Key("CLOUDBRAIN_COMING_STOP_SEND_FLAG").MustBool(true)
CloudbrainLongRunningNotifyList = strings.Split(sec.Key("CLOUDBRAIN_LONG_RUNNING_NOTIFY_LIST").MustString("GENERAL"), ",")
CloudbrainLongRunningTemplateId = sec.Key("CLOUDBRAIN_LONG_RUNNING_TEMPLATE_ID").MustString("")
CloudbrainLongRunningNotifyInterval, _ = time.ParseDuration(sec.Key("CLOUDBRAIN_LONG_RUNNING_NOTIFY_INTERVAL").MustString(""))

sec = Cfg.Section("repo-square")
IncubationSourceOrgName = sec.Key("INCUBATION_ORG_NAME").MustString("OpenI")


+ 20
- 16
modules/structs/card_requests.go View File

@@ -1,22 +1,26 @@
package structs

type CardReq struct {
ID int64 `json:"id"`
ComputeResource string `json:"compute_resource" binding:"Required"`
CardType string `json:"card_type" binding:"Required"`
AccCardsNum string `json:"acc_cards_num" binding:"Required"`
DiskCapacity int64 `json:"disk_capacity"`
ResourceType int `json:"resource_type" binding:"Required"`
BeginDate string `json:"begin_date" binding:"Required"`
EndDate string `json:"end_date" binding:"Required"`
Contact string `json:"contact" binding:"Required"`
PhoneNumber string `json:"phone_number" binding:"Required"`
EmailAddress string `json:"email_address" binding:"Required;Email;MaxSize(254)"`
Wechat string `json:"wechat" binding:"Required;MaxSize(254)"`
Org string `json:"org" binding:"MaxSize(500)"`
Description string `json:"description" binding:"MaxSize(3000)"`
Review string `json:"review"`
SpecIds []int64 `json:"spec_ids"`
ID int64 `json:"id"`
ComputeResource string `json:"compute_resource" binding:"Required"`
CardType string `json:"card_type" binding:"Required"`
AccCardsNum string `json:"acc_cards_num" binding:"Required"`
DiskCapacity int64 `json:"disk_capacity"`
ResourceType int `json:"resource_type" binding:"Required"`
BeginDate string `json:"begin_date" binding:"Required"`
EndDate string `json:"end_date" binding:"Required"`
Contact string `json:"contact" binding:"Required"`
PhoneNumber string `json:"phone_number" binding:"Required"`
EmailAddress string `json:"email_address" binding:"Required;Email;MaxSize(254)"`
Wechat string `json:"wechat" binding:"Required;MaxSize(254)"`
IsResearchProject bool `json:"is_research_project"`
InstitutionName string `json:"institution_name" binding:"MaxSize(254)"`
ProjectName string `json:"project_name" binding:"MaxSize(254)"`
ProjectCode string `json:"project_code" binding:"MaxSize(254)"`
Org string `json:"org" binding:"MaxSize(500)"`
Description string `json:"description" binding:"MaxSize(3000)"`
Review string `json:"review"`
SpecIds []int64 `json:"spec_ids"`
}

type RequestSpecInfo struct {


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

@@ -1307,6 +1307,7 @@ cloudbrain.morethanonejob=You already have a running or waiting task, create it
cloudbrain.morethanonejob1=You have created an <span style="color:rgba(242, 113, 28, 1);"> equivalent task </span> that is waiting or running, please wait for the task to finish before creating it.
cloudbrain.morethanonejob2=You can view all your Cloud Brain tasks in <a href="/cloudbrains" target="_blank"> Home > Cloudbrain Task </a>.

modelarts.general_job = General Task
modelarts.online_infer = Online Inference
modelarts.infer_job_model = Model
modelarts.infer_job_model_file = Model File
@@ -3246,6 +3247,7 @@ task_createmodel=`created new model <a href="%s/modelmanage/model_readme_tmpl?na
task_gputrainjob=`created CPU/GPU training task <a href="%s/cloudbrain/train-job/%s">%s</a>`
task_c2netnputrainjob=`created NPU training task <a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgputrainjob=`created CPU/GPU training task <a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgeneraljob=`created GPU general task <a href="%s/grampus/general/%s">%s</a>`
binded_wechat=binded WeChat
dataset_recommended=`created dataset <a href="%s/datasets">%s</a> was set as recommended dataset`
create_image=`committed image <span style="font-weight:bold;">%s</span>`


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

@@ -1319,6 +1319,7 @@ cloudbrain.morethanonejob=您已经创建了一个正在等待或运行中的同
cloudbrain.morethanonejob1=您已经有 <span style="color:rgba(242, 113, 28, 1);">同类任务</span> 正在等待或运行中,请等待任务结束再创建;
cloudbrain.morethanonejob2=可以在 “<a href="/cloudbrains" target="_blank" >个人中心 > 云脑任务</a>” 查看您所有的云脑任务。

modelarts.general_job = 通用任务
modelarts.online_infer = 在线推理
modelarts.infer_job_model = 模型名称
modelarts.infer_job_model_file = 模型文件
@@ -3265,6 +3266,7 @@ task_createmodel=`导入了新模型 <a href="%s/modelmanage/model_readme_tmpl?n
task_gputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/cloudbrain/train-job/%s">%s</a>`
task_c2netnputrainjob=`创建了NPU类型训练任务 <a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgeneraljob=`创建了GPU类型通用任务 <a href="%s/grampus/general/%s">%s</a>`
binded_wechat=绑定微信
dataset_recommended=`创建的数据集 <a href="%s/datasets">%s</a> 被设置为推荐数据集`
create_image=`提交了镜像 <span style="font-weight:bold;">%s</span>`


+ 9
- 1
public/home/home.js View File

@@ -255,7 +255,7 @@ document.onreadystatechange = function () {
}
else if(record.OpType == "25" || record.OpType == "29" || record.OpType == "39" || record.OpType == "40" || record.OpType == "41"
|| record.OpType == "43"|| record.OpType == "44"|| record.OpType == "45"|| record.OpType == "46"|| record.OpType == "47"
|| record.OpType == "48"|| record.OpType == "49" || record.OpType == "53"
|| record.OpType == "48"|| record.OpType == "49" || record.OpType == "53" || record.OpType == "54"
){
html += recordPrefix + actionName;
const taskLink = getTaskLink(record);
@@ -348,6 +348,12 @@ function getTaskLink(record){
re = re + "/supercompute/job/" + record.Cloudbrain.ID;
} else if(record.OpType == 45){
re = re + "/grampus/onlineinfer/" + record.Content;
} else if(record.OpType == 54){
if (record.Cloudbrain) {
re = re + "/grampus/general/" + record.Cloudbrain.ID;
} else {
re = '';
}
}
re = encodeURI(re);
return re;
@@ -519,6 +525,7 @@ var actionNameZH={
"48":"创建了ILUVATAR-GPGPU类型调试任务",
"49":"创建了METAX-GPGPU类型调试任务",
"53":"创建了ILUVATAR-GPGPU类型训练任务",
"54":"创建了GPU类型通用任务",
};

var actionNameEN={
@@ -564,6 +571,7 @@ var actionNameEN={
"48":" created ILUVATAR-GPGPU type debugging task ",
"49":" created METAX-GPGPU type debugging task ",
"53":" created ILUVATAR-GPGPU type training task ",
"54":" created GPU type general task ",
};

var repoAndOrgZH={


+ 0
- 91
routers/admin/cloudbrains.go View File

@@ -4,7 +4,6 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"time"

"github.com/360EntSecGroup-Skylar/excelize/v2"
@@ -16,7 +15,6 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
)

@@ -29,98 +27,9 @@ const (
)

func CloudBrains(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.cloudBrains")
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminCloudBrains"] = true

listType := ctx.Query("listType")
jobType := ctx.Query("jobType")
jobStatus := ctx.Query("jobStatus")
aiCenter := ctx.Query("aiCenter")
cluster := ctx.Query("cluster")

ctx.Data["ListType"] = listType
ctx.Data["JobType"] = jobType
ctx.Data["JobStatus"] = jobStatus
ctx.Data["aiCenter"] = aiCenter
ctx.Data["cluster"] = cluster

page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}

var jobTypes []string
jobTypeNot := false
if jobType == string(models.JobTypeBenchmark) {
jobTypes = append(jobTypes, models.AllBenchMarkJobType()...)
} else if jobType != "all" && jobType != "" {
jobTypes = append(jobTypes, jobType)
}

var jobStatuses []string
jobStatusNot := false
if jobStatus == "other" {
jobStatusNot = true
jobStatuses = append(jobStatuses, string(models.ModelArtsTrainJobWaiting), string(models.ModelArtsTrainJobFailed), string(models.ModelArtsRunning), string(models.ModelArtsTrainJobCompleted),
string(models.ModelArtsStarting), string(models.ModelArtsRestarting), string(models.ModelArtsStartFailed),
string(models.ModelArtsStopping), string(models.ModelArtsStopped), string(models.JobSucceeded))
} else if jobStatus != "all" && jobStatus != "" {
jobStatuses = append(jobStatuses, jobStatus)
}

keyword := strings.Trim(ctx.Query("q"), " ")

ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
},
Keyword: keyword,
JobTypeNot: jobTypeNot,
JobStatusNot: jobStatusNot,
JobStatus: jobStatuses,
JobTypes: jobTypes,
NeedRepoInfo: true,
IsLatestVersion: modelarts.IsLatestVersion,
ComputeResource: listType,
Type: models.TypeCloudBrainAll,
AiCenter: aiCenter,
Cluster: cluster,
})
if err != nil {
ctx.ServerError("Get job failed:", err)
return
}

models.LoadSpecs4CloudbrainInfo(ciTasks)

for i, task := range ciTasks {
ciTasks[i] = cloudbrainService.UpdateCloudbrainAiCenter(ciTasks[i])
ciTasks[i].Cloudbrain.AiCenter = repo.GetAiCenterNameByCode(ciTasks[i].Cloudbrain.AiCenter, ctx.Language())
ciTasks[i].CanDebug = true
ciTasks[i].CanDel = true
ciTasks[i].Cloudbrain.ComputeResource = task.ComputeResource
if ciTasks[i].Cloudbrain.Spec != nil {
if ciTasks[i].Cloudbrain.Type == models.TypeC2Net {
ciTasks[i].Cloudbrain.Spec.Cluster = models.C2NetCluster
} else {
ciTasks[i].Cloudbrain.Spec.Cluster = models.OpenICluster
}
}
}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, getTotalPage(count, setting.UI.IssuePagingNum))
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "listType", "ListType")
ctx.Data["Page"] = pager
ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = ciTasks
ctx.Data["CanCreate"] = true
ctx.Data["Keyword"] = keyword

ctx.HTML(200, tplCloudBrains)

}

func Images(ctx *context.Context) {


+ 108
- 1
routers/ai_task/ai_task.go View File

@@ -256,6 +256,7 @@ func GetAITaskInfo(ctx *context.Context) {
CanDownload: cloudbrain.CanDownloadJob(ctx, job),
EarlyVersionList: earlyVersionList,
CanCreateVersion: job.CanUserModify(ctx.User),
CanModify: job.CanUserModify(ctx.User),
}
//根据权限去掉数据集和模型信息
res.TryToRemoveDatasetAndModelInfo(ctx.User)
@@ -279,6 +280,7 @@ func GetAITaskBriefInfo(ctx *context.Context) {
return
}
res.Tr(ctx.Language())
res.ClearNonPublicFields()
ctx.JSON(http.StatusOK, response.OuterSuccessWithData(res))
}

@@ -468,7 +470,7 @@ func GetAITaskList(ctx *context.Context) {
if jobType != "" {
jobTypes = append(jobTypes, jobType)
}
result, err := task.GetAITaskList(entity.GetTaskListReq{
result, err := task.GetRepoAITaskList(entity.GetTaskListReq{
ListOptions: models.ListOptions{
PageSize: setting.UI.IssuePagingNum,
Page: page,
@@ -486,6 +488,9 @@ func GetAITaskList(ctx *context.Context) {
}
result.CanCreateTask = cloudbrain.CanCreateOrDebugJob(ctx)
result.IsRepoEmpty = ctx.Repo.Repository.IsEmpty
for _, r := range result.Tasks {
r.Task.Tr(ctx.Language())
}
ctx.JSON(http.StatusOK, response.OuterSuccessWithData(result))
}

@@ -576,3 +581,105 @@ func GenerateSDKCode(ctx *context.Context) {
code := task.GenerateSDKCode(datasetNames, pretrainModelNames, parameterKeys, models.JobType(jobType))
ctx.JSON(http.StatusOK, response.OuterSuccessWithData(map[string]string{"code": code}))
}

func GetMyAITaskList(ctx *context.Context) {
jobType := ctx.Query("job_type")
jobStatus := ctx.Query("job_status")
aiCenter := ctx.Query("ai_center")
cluster := ctx.Query("cluster")
computeSourceName := ctx.Query("compute_source")
excludeStatus := []string{}
if jobStatus == "other" {
statusStr := ctx.Query("exclude_status")
if statusStr == "" {
ctx.JSON(http.StatusOK, response.PARAM_ERROR)
}
excludeStatus = strings.Split(statusStr, ",")
}
keyword := strings.Trim(ctx.Query("q"), " ")
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 || pageSize > 100 {
pageSize = setting.UI.IssuePagingNum
}
computeSource := models.GetComputeSourceInstance(computeSourceName)
result, err := task.GetMyAITaskList(entity.GetMyTaskListReq{
ListOptions: models.ListOptions{
PageSize: pageSize,
Page: page,
},
ComputeSource: computeSource,
JobType: jobType,
User: ctx.User,
IsRepoOwner: ctx.Repo.IsOwner(),
JobStatus: jobStatus,
Keyword: keyword,
AICenter: aiCenter,
Cluster: cluster,
ExcludeStatus: excludeStatus,
})
if err != nil {
log.Error("GetMyAITaskList error,err=%v", err)
ctx.JSON(http.StatusOK, response.OuterTrBizError(err, ctx))
return
}
result.CanCreateTask = cloudbrain.CanCreateOrDebugJob(ctx)
for _, r := range result.Tasks {
r.Task.Tr(ctx.Language())
}
ctx.JSON(http.StatusOK, response.OuterSuccessWithData(result))
}

func GetAITaskList4Admin(ctx *context.Context) {
jobType := ctx.Query("job_type")
jobStatus := ctx.Query("job_status")
aiCenter := ctx.Query("ai_center")
cluster := ctx.Query("cluster")
computeSourceName := ctx.Query("compute_source")
excludeStatus := []string{}
if jobStatus == "other" {
statusStr := ctx.Query("exclude_status")
if statusStr == "" {
ctx.JSON(http.StatusOK, response.PARAM_ERROR)
}
excludeStatus = strings.Split(statusStr, ",")
}

keyword := strings.Trim(ctx.Query("q"), " ")
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 || pageSize > 100 {
pageSize = setting.UI.IssuePagingNum
}
computeSource := models.GetComputeSourceInstance(computeSourceName)
result, err := task.GetAITaskList4Admin(entity.GetMyTaskListReq{
ListOptions: models.ListOptions{
PageSize: pageSize,
Page: page,
},
ComputeSource: computeSource,
JobType: jobType,
User: ctx.User,
JobStatus: jobStatus,
Keyword: keyword,
AICenter: aiCenter,
Cluster: cluster,
ExcludeStatus: excludeStatus,
})
if err != nil {
log.Error("GetMyAITaskList error,err=%v", err)
ctx.JSON(http.StatusOK, response.OuterTrBizError(err, ctx))
return
}
result.CanCreateTask = cloudbrain.CanCreateOrDebugJob(ctx)
for _, r := range result.Tasks {
r.Task.Tr(ctx.Language())
}
ctx.JSON(http.StatusOK, response.OuterSuccessWithData(result))
}

+ 4
- 0
routers/api/v1/api.go View File

@@ -680,6 +680,7 @@ func RegisterRoutes(m *macaron.Macaron) {

m.Group("/ai_task", func() {
m.Get("/generate_sdk_code", ai_task.GenerateSDKCode)
m.Get("/my_list", reqToken(), ai_task.GetMyAITaskList)
})

// Miscellaneous
@@ -1470,6 +1471,9 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Combo("").Post(bind(models.OperateRoleReq{}), admin.AddUserRole).
Delete(bind(models.OperateRoleReq{}), admin.DeleteUserRole)
})
m.Group("/ai_task", func() {
m.Get("/list", ai_task.GetAITaskList4Admin)
})
}, reqToken(), reqSiteAdmin())

m.Group("/topics", func() {


+ 10
- 0
routers/api/v1/repo/images.go View File

@@ -119,6 +119,16 @@ func GetAvailableFilerInfo(ctx *context.APIContext) {
IncludeStarByMe: starByMe,
UID: getUID(ctx),
}

if ctx.QueryInt64("spec") > 0 {
spec, err := models.FindSpecs(models.FindSpecsOptions{
SpecId: ctx.QueryInt64("spec"),
})
if err == nil {
opts.AiCenterId = spec[0].AiCenterCode
}
}

ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{
Data: models.GetImageAvailableColumnValues(opts),
})


+ 17
- 12
routers/card_request/card_request.go View File

@@ -153,18 +153,19 @@ func GetAdminCardRequestList(ctx *context.Context) {
useBeginTime.Unix()

opts := &models.CardRequestOptions{
OrderBy: models.OrderByStatus,
NeedSpec: true,
Keyword: strings.Trim(ctx.Query("q"), " "),
AiCenterCode: ctx.Query("center"),
Cluster: ctx.Query("cluster"),
ComputeResource: ctx.Query("resource"),
AccCardType: ctx.Query("cardType"),
QueueId: ctx.QueryInt64("queue"),
UseBeginTime: getTimeUnix(ctx.Query("useBeginTime")),
UseEndTime: getTimeUnix(ctx.Query("useEndTime")),
BeginTimeUnix: getTimeUnix(ctx.Query("beginTime")),
EndTimeUnix: getTimeUnix(ctx.Query("endTime")),
OrderBy: models.OrderByStatus,
NeedSpec: true,
Keyword: strings.Trim(ctx.Query("q"), " "),
AiCenterCode: ctx.Query("center"),
Cluster: ctx.Query("cluster"),
ComputeResource: ctx.Query("resource"),
AccCardType: ctx.Query("cardType"),
QueueId: ctx.QueryInt64("queue"),
IsResearchProject: ctx.QueryInt("isResearchProject"),
UseBeginTime: getTimeUnix(ctx.Query("useBeginTime")),
UseEndTime: getTimeUnix(ctx.Query("useEndTime")),
BeginTimeUnix: getTimeUnix(ctx.Query("beginTime")),
EndTimeUnix: getTimeUnix(ctx.Query("endTime")),
}
opts.ListOptions = models.ListOptions{
Page: page,
@@ -306,6 +307,10 @@ func getRequestShowList(ctx *context.Context, opts *models.CardRequestOptions, c
customShow.PhoneNumber = v.PhoneNumber
customShow.EmailAddress = v.EmailAddress
customShow.Wechat = v.Wechat
customShow.IsResearchProject = v.IsResearchProject
customShow.InstitutionName = v.InstitutionName
customShow.ProjectName = v.ProjectName
customShow.ProjectCode = v.ProjectCode
customShow.Contact = v.Contact
customShow.Specs = v.Specs
customShow.Org = v.Org


+ 5
- 99
routers/repo/cloudbrain.go View File

@@ -1775,6 +1775,7 @@ func GetImages(ctx *context.Context, opts *models.SearchImageOptions) {
opts.OperationSystem = ctx.Query("os")
opts.OperationSystemVersion = ctx.Query("osVersion")
opts.ThirdPackages = ctx.Query("thirdParty")
opts.OnlyOpenIImage = ctx.QueryBool("onlyOpenIImage")

if ctx.QueryInt64("spec") > 0 {
spec, err := models.FindSpecs(models.FindSpecsOptions{
@@ -2136,107 +2137,12 @@ func SyncCloudbrainStatus() {
if task.JobType == string(models.JobTypeModelSafety) {
continue
}
if task.IsNewAITask() {
task, _ = ai_task.UpdateCloudbrain(task)
if task.Duration >= setting.MaxDuration && task.JobType == string(models.JobTypeDebug) {
ai_task.StopCloudbrain(task)
}

continue
}
if task.Type == models.TypeCloudBrainOne {
task, err = cloudbrainTask.SyncCloudBrainOneStatus(task)
if err != nil {
log.Error("Sync cloud brain one (%s) failed:%v", task.JobName, err)
continue
}

} else if task.Type == models.TypeCloudBrainTwo || task.Type == models.TypeCDCenter {
if task.JobType == string(models.JobTypeDebug) {
err := modelarts.HandleNotebookInfo(task)
if err != nil {
log.Error("HandleNotebookInfo(%s) failed:%v", task.DisplayJobName, err)
continue
}
} else if task.JobType == string(models.JobTypeTrain) || task.JobType == string(models.JobTypeInference) {
err := modelarts.HandleTrainJobInfo(task)
if err != nil {
log.Error("HandleTrainJobInfo(%s) failed:%v", task.DisplayJobName, err)
continue
}
} else {
log.Error("task.JobType(%s) is error:%s", task.DisplayJobName, task.JobType)
}
} else if task.Type == models.TypeC2Net {
if task.JobType == string(models.JobTypeDebug) || task.JobType == string(models.JobTypeSuperCompute) {
cloudbrainTask.SyncGrampusNotebookStatus(task)
} else {
result, err := grampus.GetJob(task.JobID)
if err != nil {
log.Error("GetTrainJob(%s) failed:%v", task.DisplayJobName, err)
continue
}

if result != nil {
if len(result.JobInfo.Tasks) > 0 {
if len(result.JobInfo.Tasks[0].CenterID) == 1 && len(result.JobInfo.Tasks[0].CenterName) == 1 {
task.AiCenter = result.JobInfo.Tasks[0].CenterID[0] + "+" + result.JobInfo.Tasks[0].CenterName[0]
}
}
oldStatus := task.Status
task.Status = grampus.TransTrainJobStatus(result.JobInfo.Status)
task.Duration = result.JobInfo.RunSec

if task.Duration < 0 {
task.Duration = 0
}
task.TrainJobDuration = models.ConvertDurationToStr(task.Duration)

if task.StartTime == 0 && result.JobInfo.StartedAt > 0 {
task.StartTime = timeutil.TimeStamp(result.JobInfo.StartedAt)
}
if task.EndTime == 0 && models.IsTrainJobTerminal(task.Status) && task.StartTime > 0 {
task.EndTime = task.StartTime.Add(task.Duration)
}
task.CorrectCreateUnix()
if oldStatus != task.Status {
notification.NotifyChangeCloudbrainStatus(task, oldStatus)
}
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
continue
}
}
}
} else {
log.Error("task.Type(%s) is error:%d", task.JobName, task.Type)
}
if task.Status != string(models.JobWaiting) {
if (task.Duration >= setting.MaxDuration && task.JobType == string(models.JobTypeDebug)) || (task.Duration >= setting.Grampus.MMLSparkMaxTime && task.JobType == string(models.JobTypeSuperCompute)) {
log.Info("begin to stop job(%s), because of the duration", task.DisplayJobName)
err = cloudbrainTask.StopDebugJob(task)
if err != nil {
log.Error("StopJob(%s) failed:%v", task.DisplayJobName, err)
continue
}
oldStatus := task.Status
task.Status = string(models.JobStopped)
if task.EndTime == 0 {
task.EndTime = timeutil.TimeStampNow()
}
task.ComputeAndSetDuration()
if oldStatus != task.Status {
notification.NotifyChangeCloudbrainStatus(task, oldStatus)
}
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err)
continue
}
}

task, _ = ai_task.UpdateCloudbrain(task)
if task.Duration >= setting.MaxDuration && task.JobType == string(models.JobTypeDebug) {
ai_task.StopCloudbrain(task)
}
go ai_task.TryToNotifyLongRunningTask(task)
}

return


+ 28
- 0
routers/repo/grampus_general.go View File

@@ -0,0 +1,28 @@
package repo

import (
"net/http"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
)

const (
tplGrampusGeneralIndex base.TplName = "repo/grampus/general/list"
tplGrampusGeneralShow base.TplName = "repo/grampus/general/show"
tplGrampusGeneralNew base.TplName = "repo/grampus/general/new"
)

func GrampusGeneralNew(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true
ctx.HTML(http.StatusOK, tplGrampusGeneralNew)
}

func GrampusGeneralShow(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true
ctx.HTML(http.StatusOK, tplGrampusGeneralShow)
}

func GrampusGeneralIndex(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true
ctx.HTML(http.StatusOK, tplGrampusGeneralIndex)
}

+ 8
- 0
routers/routes/routes.go View File

@@ -1381,6 +1381,14 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, context.PointAccount(), repo.GrampusOnlineInferNew)
})

m.Group("/general", func() {
m.Get("", reqRepoCloudBrainReader, repo.GrampusGeneralIndex)
m.Group("/:id", func() {
m.Get("", reqRepoCloudBrainReader, repo.GrampusGeneralShow)
})
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, context.PointAccount(), repo.GrampusGeneralNew)
})

m.Group("/train-job", func() {
m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.GrampusTrainJobShow)


+ 0
- 108
routers/user/home.go View File

@@ -20,11 +20,8 @@ import (
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/repo"
cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"

@@ -758,112 +755,7 @@ func Email2User(ctx *context.Context) {
}

func Cloudbrains(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("user.cloudbrains")

listType := ctx.Query("listType")
jobType := ctx.Query("jobType")
jobStatus := ctx.Query("jobStatus")
aiCenter := ctx.Query("aiCenter")
cluster := ctx.Query("cluster")

ctx.Data["ListType"] = listType
ctx.Data["JobType"] = jobType
ctx.Data["JobStatus"] = jobStatus
ctx.Data["aiCenter"] = aiCenter
ctx.Data["cluster"] = cluster

page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}

var jobTypes []string
jobTypeNot := false
if jobType == string(models.JobTypeBenchmark) {
jobTypes = models.AllBenchMarkJobType()
} else if jobType != "all" && jobType != "" {
jobTypes = append(jobTypes, jobType)
}

var jobStatuses []string
jobStatusNot := false
if jobStatus == "other" {
jobStatusNot = true
jobStatuses = append(jobStatuses, string(models.ModelArtsTrainJobWaiting), string(models.ModelArtsTrainJobFailed), string(models.ModelArtsRunning), string(models.ModelArtsTrainJobCompleted),
string(models.ModelArtsStarting), string(models.ModelArtsRestarting), string(models.ModelArtsStartFailed),
string(models.ModelArtsStopping), string(models.ModelArtsStopped), string(models.JobSucceeded))
} else if jobStatus != "all" && jobStatus != "" {
jobStatuses = append(jobStatuses, jobStatus)
}

keyword := strings.Trim(ctx.Query("q"), " ")

ctxUser := getDashboardContextUser(ctx)
if ctx.Written() {
return
}
repos, _, err := models.SearchRepository(&models.SearchRepoOptions{
Actor: ctx.User,
OwnerID: ctxUser.ID,
Private: true,
})
if err != nil {
ctx.ServerError("SearchRepository", err)
return
}
var repoIDList []int64
for i, _ := range repos {
repoIDList = append(repoIDList, repos[i].ID)
}
ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
},
Keyword: keyword,
UserID: ctxUser.ID,
JobTypeNot: jobTypeNot,
JobStatusNot: jobStatusNot,
JobStatus: jobStatuses,
JobTypes: jobTypes,
NeedRepoInfo: true,
IsLatestVersion: modelarts.IsLatestVersion,
RepoIDList: repoIDList,
ComputeResource: listType,
Type: models.TypeCloudBrainAll,
AiCenter: aiCenter,
Cluster: cluster,
})
if err != nil {
ctx.ServerError("Get job failed:", err)
return
}
models.LoadSpecs4CloudbrainInfo(ciTasks)
for i, _ := range ciTasks {
ciTasks[i] = cloudbrainService.UpdateCloudbrainAiCenter(ciTasks[i])
ciTasks[i].Cloudbrain.AiCenter = repo.GetAiCenterNameByCode(ciTasks[i].Cloudbrain.AiCenter, ctx.Language())
ciTasks[i].CanDebug = true
ciTasks[i].CanDel = true
ciTasks[i].Cloudbrain.ComputeResource = ciTasks[i].ComputeResource
if ciTasks[i].Cloudbrain.Spec != nil {
if ciTasks[i].Cloudbrain.Type == models.TypeC2Net {
ciTasks[i].Cloudbrain.Spec.Cluster = models.C2NetCluster
} else {
ciTasks[i].Cloudbrain.Spec.Cluster = models.OpenICluster
}
}
}
pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, getTotalPage(count, setting.UI.IssuePagingNum))
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "listType", "ListType")
ctx.Data["Page"] = pager
ctx.Data["PageIsUserCloudBrain"] = true
ctx.Data["Tasks"] = ciTasks
ctx.Data["CanCreate"] = true
ctx.Data["Keyword"] = keyword

ctx.HTML(200, tplCloudbrains)

}
func getTotalPage(total int64, pageSize int) int {



+ 80
- 0
services/ai_task_service/cluster/c2net.go View File

@@ -67,6 +67,25 @@ func (c C2NetClusterAdapter) CreateOnlineInfer(req entity.CreateNoteBookTaskRequ
return convertGrampus2NoteBookRes(jobResult), nil
}

func (c C2NetClusterAdapter) CreateGeneralTask(req entity.CreateGeneralTaskRequest) (*entity.CreateGeneralTaskResponse, error) {
log.Info("start to CreateGeneralTask req=%+v", req)
newReq, err := convertGeneralTaskReq2Grampus(req)
if err != nil {
log.Error("CreateGeneralTask err.req=%+v err=%v", req, err)
return nil, err
}
jobResult, err := grampus.CreateNotebookJob(newReq)
if err != nil {
log.Error("CreateGeneralTask failed: %v", err.Error())
return nil, err
}
if jobResult.ErrorCode > 0 {
log.Error("CreateGeneralTask call grampus err.req.Name = %s ErrorCode = %d ErrorMsg = %s", req.Name, jobResult.ErrorCode, jobResult.ErrorMsg)
return nil, errors.New(fmt.Sprintf("CreateGeneralTask err[%d%s]", jobResult.ErrorCode, jobResult.ErrorMsg))
}
return convertGrampus2GeneralTaskRes(jobResult), nil
}

func (c C2NetClusterAdapter) GetNotebookImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error) {
processType := req.ComputeSource.FullName
images, err := grampus.GetImages(processType, string(req.JobType))
@@ -166,6 +185,20 @@ func convertOnlineInfer2Grampus(req entity.CreateNoteBookTaskRequest) (models.Cr
return models.CreateGrampusInferenceRequest{Name: req.Name, Tasks: tasks}, nil
}

func convertGeneralTaskReq2Grampus(req entity.CreateGeneralTaskRequest) (models.CreateGrampusNotebookRequest, error) {
tasks := make([]models.GrampusNotebookTask, len(req.Tasks))
for i := 0; i < len(req.Tasks); i++ {
t := req.Tasks[i]
task, err := convertGeneralTask2Grampus(t)
if err != nil {
return models.CreateGrampusNotebookRequest{}, err
}
tasks[i] = task
}

return models.CreateGrampusNotebookRequest{Name: req.Name, Tasks: tasks}, nil
}

func generateCommand(repoName, bootFile, datasetName string) string {

//prepare
@@ -280,6 +313,37 @@ func convertNoteBookTask2Grampus(t entity.NoteBookTask, command string) (models.
}, nil
}

func convertGeneralTask2Grampus(t entity.GeneralTask) (models.GrampusNotebookTask, error) {
code := models.GrampusDataset{}
codeArray := convertContainerArray2GrampusArray(t.Code)
if codeArray != nil && len(codeArray) > 0 {
code = codeArray[0]
}
output := models.GrampusDataset{}
outputArray := convertContainerArray2GrampusArray(t.OutPut)
if outputArray != nil && len(outputArray) > 0 {
output = outputArray[0]
}
centerIds, err := getGrampusAvailableCenterIds(t.Queues, t.ImageId, *models.GetComputeSourceInstance(t.Spec.ComputeResource), models.JobTypeDebug)
if err != nil {
return models.GrampusNotebookTask{}, err
}
return models.GrampusNotebookTask{
Name: t.Name,
ResourceSpecId: t.Spec.SourceSpecId,
ImageId: t.ImageId,
ImageUrl: t.ImageUrl,
Datasets: convertContainerArray2GrampusArray(t.Datasets),
PreTrainModel: convertContainerArray2GrampusArray(t.PreTrainModel),
Code: code,
OutPut: output,
EnvVariables: t.EnvVariables,
AutoStopDuration: t.AutoStopDuration,
Capacity: t.Capacity,
CenterID: centerIds,
}, nil
}

func getGrampusAvailableCenterIds(queues []models.ResourceQueue, imageId string, computeSource models.ComputeSource, jobType models.JobType) ([]string, error) {
if len(queues) == 0 {
return []string{}, nil
@@ -399,6 +463,22 @@ func convertGrampus2NoteBookRes(res *models.GrampusNotebookResponse) *entity.Cre
}
}

func convertGrampus2GeneralTaskRes(res *models.GrampusNotebookResponse) *entity.CreateGeneralTaskResponse {
jobInfo := res.JobInfo
return &entity.CreateGeneralTaskResponse{
StartedAt: jobInfo.StartedAt,
RunSec: jobInfo.RunSec,
CompletedAt: jobInfo.CompletedAt,
CreatedAt: jobInfo.CreatedAt,
UpdatedAt: jobInfo.UpdatedAt,
Desc: jobInfo.Desc,
JobID: jobInfo.JobID,
Name: jobInfo.Name,
Status: jobInfo.Status,
UserID: jobInfo.UserID,
}
}

func (c C2NetClusterAdapter) RestartNoteBook(jobId string) (*entity.RestartNoteBookTaskResponse, error) {
res, err := grampus.RestartNotebookJob(jobId)
if err != nil {


+ 4
- 1
services/ai_task_service/cluster/cloudbrain_one.go View File

@@ -1,6 +1,5 @@
package cluster

import "C"
import (
"encoding/json"
"errors"
@@ -38,6 +37,10 @@ func (c CloudbrainOneClusterAdapter) CreateOnlineInfer(req entity.CreateNoteBook
return nil, nil
}

func (c CloudbrainOneClusterAdapter) CreateGeneralTask(req entity.CreateGeneralTaskRequest) (*entity.CreateGeneralTaskResponse, error) {
return nil, nil
}

func (c CloudbrainOneClusterAdapter) GetNotebookImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error) {
return nil, true, nil
}


+ 4
- 0
services/ai_task_service/cluster/cloudbrain_two.go View File

@@ -102,6 +102,10 @@ func (c CloudbrainTwoClusterAdapter) CreateOnlineInfer(req entity.CreateNoteBook
return nil, nil
}

func (c CloudbrainTwoClusterAdapter) CreateGeneralTask(req entity.CreateGeneralTaskRequest) (*entity.CreateGeneralTaskResponse, error) {
return nil, nil
}

var cloudbrainTwoNotebookImages []entity.ClusterImage

func (c CloudbrainTwoClusterAdapter) GetNotebookImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error) {


+ 1
- 0
services/ai_task_service/cluster/cluster_base.go View File

@@ -51,4 +51,5 @@ type ClusterAdapter interface {
GetNotebookImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error)
GetTrainImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error)
CreateOnlineInfer(req entity.CreateNoteBookTaskRequest) (*entity.CreateNoteBookTaskResponse, error)
CreateGeneralTask(req entity.CreateGeneralTaskRequest) (*entity.CreateGeneralTaskResponse, error)
}

+ 2
- 0
services/ai_task_service/task/cloudbrain_one_notebook_task.go View File

@@ -36,6 +36,7 @@ func GetCloudbrainOneNotebookConfig(opts entity.AITaskConfigKey) *entity.AITaskB
DatasetsMaxNum: setting.MaxDatasetNum,
ModelLimitSizeGB: setting.DEBUG_MODEL_SIZE_LIMIT_GB,
ModelMaxNum: setting.DEBUG_MODEL_NUM_LIMIT,
DebugAddressCheck: true,
ContainerSteps: map[entity.ContainerDataType]*entity.ContainerBuildOpts{
entity.ContainerCode: {
ContainerPath: "/code",
@@ -75,6 +76,7 @@ func GetCloudbrainOneNotebookConfig(opts entity.AITaskConfigKey) *entity.AITaskB
Uncompressed: true,
},
},
DebugAddressCheck: true,
}

}


+ 166
- 0
services/ai_task_service/task/grampus_general_task.go View File

@@ -0,0 +1,166 @@
package task

import (
"strings"

"code.gitea.io/gitea/entity"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/routers/response"
"code.gitea.io/gitea/services/ai_task_service/context"
)

type GrampusGeneralTaskTemplate struct {
DefaultAITaskTemplate
}

func init() {
t := &GrampusGeneralTaskTemplate{
DefaultAITaskTemplate: DefaultAITaskTemplate{
ClusterType: entity.C2Net,
JobType: models.JobTypeGeneral,
Config: GetGeneralTaskConfig,
},
}
RegisterTask(models.JobTypeGeneral, entity.C2Net, t)
}

func GetGeneralTaskConfig(opts entity.AITaskConfigKey) *entity.AITaskBaseConfig {
codePath := "/code"
datasetPath := "/dataset"
pretrainModelPath := "/pretrainmodel"

config := &entity.AITaskBaseConfig{
ContainerSteps: map[entity.ContainerDataType]*entity.ContainerBuildOpts{
entity.ContainerCode: {
ContainerPath: codePath,
ReadOnly: false,
AcceptStorageType: []entity.StorageType{entity.MINIO, entity.OBS},
VolumeFolder: true,
},
entity.ContainerDataset: {
ContainerPath: datasetPath,
ReadOnly: true,
AcceptStorageType: []entity.StorageType{entity.MINIO, entity.OBS},
},
entity.ContainerPreTrainModel: {
ContainerPath: pretrainModelPath,
ReadOnly: true,
AcceptStorageType: []entity.StorageType{entity.MINIO, entity.OBS},
},
},
ActionType: models.ActionCreateGeneralGPUTask,
DatasetsLimitSizeGB: setting.DebugAttachSize,
DatasetsMaxNum: setting.MaxDatasetNum,
ModelLimitSizeGB: setting.DEBUG_MODEL_SIZE_LIMIT_GB,
ModelMaxNum: setting.DEBUG_MODEL_NUM_LIMIT,
DebugAddressCheck: true,
}
return config
}

func (t GrampusGeneralTaskTemplate) Create(ctx *context.CreationContext) (*entity.CreateTaskRes, *response.BizError) {
c := &CreateOperator{}
err := c.Next(t.CheckParamFormat).
Next(t.CheckMultiRequest).
Next(t.CheckDisplayJobName).
//Next(t.CheckNotebookCount).
Next(t.LoadSpec).
Next(t.CheckPointBalance).
Next(t.CheckDatasets).
Next(t.CheckBranchExists).
Next(t.CheckModels).
Next(t.InsertCloudbrainRecord4Async).
AsyncNextWithErrFun(t.BuildContainerData, t.GetAvailableQueues, t.CallCreationAPI, t.AfterCallCreationAPI4Async, t.NotifyCreation, t.HandleErr4Async).
Operate(ctx)
if err != nil {
log.Error("create GrampusNoteBookTask err.%v", err)
return nil, err
}
return &entity.CreateTaskRes{ID: ctx.NewCloudbrain.ID}, nil
}

func (g GrampusGeneralTaskTemplate) CallCreationAPI(ctx *context.CreationContext) *response.BizError {
c := g.GetMyCluster()
if c == nil {
log.Error("Get cluster failed")
return response.SYSTEM_ERROR
}
form := ctx.Request
imageUrl := strings.TrimSpace(form.ImageUrl)
if form.ImageID != "" {
imageUrl = ""
}
req := entity.CreateGeneralTaskRequest{
Name: form.JobName,
Tasks: []entity.GeneralTask{
{
Name: form.JobName,
ResourceSpecId: ctx.Spec.SourceSpecId,
ImageId: form.ImageID,
ImageUrl: imageUrl,
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
EnvVariables: models.GrampusEnvVarReq{},
Capacity: setting.Capacity,
Queues: ctx.Queues,
Spec: ctx.Spec,
},
},
}
createTime := timeutil.TimeStampNow()
res, err := c.CreateGeneralTask(req)
if err != nil {
log.Error("GrampusGeneralTaskTemplate CreateGeneralTask err.req=%+v err=%v", req, err)
return response.NewBizError(err)
}
if res.JobID == "" {
log.Error("GrampusGeneralTaskTemplate CreateGeneralTask failed.Cloudbrain.JobID=%s", ctx.SourceCloudbrain.JobID)
return response.CREATE_FAILED
}
ctx.Response = &entity.CreationResponse{
JobID: res.JobID,
Status: res.Status,
CreateTime: createTime,
}
return nil
}

func (g GrampusGeneralTaskTemplate) GetImages(computeSource models.ComputeSource, centerId ...string) ([]entity.ClusterImage, bool, *response.BizError) {
c := g.GetMyCluster()
if c == nil {
log.Error("Get cluster failed")
return nil, false, response.SYSTEM_ERROR
}

var images []entity.ClusterImage
var customFlag bool
var err error
images, customFlag, err = c.GetNotebookImages(entity.GetImageReq{
ComputeSource: computeSource,
JobType: models.JobTypeDebug,
}, centerId...)
if err != nil {
log.Error("GetImages err.computeSource=%s err =%v", computeSource.Name, err)
return nil, false, response.NewBizError(err)
}
return images, customFlag, nil
}

func (g GrampusGeneralTaskTemplate) GetOperationProfile(cloudbrainId int64) (*entity.OperationProfile, *response.BizError) {
c := g.GetMyCluster()
if c == nil {
log.Error("Get cluster failed,cloudbrainId=%d", cloudbrainId)
return nil, response.SYSTEM_ERROR
}
s, err := GetOperationProfile(cloudbrainId, c.GetNoteBookOperationProfile)
if err != nil {
log.Error("GetOperationProfile err.cloudbrainId=%d err =%v", cloudbrainId, err)
return nil, nil
}
return s, nil
}

+ 45
- 3
services/ai_task_service/task/task_base.go View File

@@ -2,6 +2,8 @@ package task

import (
"errors"
"net/http"
"strings"
"time"

cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
@@ -175,6 +177,25 @@ func (g DefaultAITaskTemplate) GetAllowedWorkerNum(userId int64, computeSource *
return []int{1}, nil
}

func (g DefaultAITaskTemplate) CheckDebugAddress(cloudbrain *models.Cloudbrain) bool {
config := g.GetConfig(entity.AITaskConfigKey{
ComputeSource: cloudbrain.GetStandardComputeSource(),
IsFileNoteBookRequest: cloudbrain.IsFileNoteBookTask(),
})
if !config.DebugAddressCheck {
return true
}
url, err := g.GetDebugUrl(cloudbrain.ID)
if err != nil || url == "" {
return false
}
res, httpErr := http.Get(url)
if httpErr != nil || res.StatusCode != http.StatusOK {
return false
}
return true
}

func (g DefaultAITaskTemplate) Query(cloudbrainId int64) (*entity.AITaskDetailInfo, *response.BizError) {
//查询时先更新,然后再查询本地数据
g.Update(cloudbrainId)
@@ -307,14 +328,35 @@ func (g DefaultAITaskTemplate) Update(cloudbrainId int64) *response.BizError {
log.Info("AI task is preparing.No need to update from remote.cloudbrainId=%d", cloudbrainId)
return nil
}

//查询任务在集群上的状态
log.Info("start to UpdateAITaskFromRemote.task.DisplayJobName = %s task.Status = %s", cloudbrain.DisplayJobName, cloudbrain.Status)
var res *entity.QueryTaskResponse
if g.JobType == models.JobTypeDebug || g.JobType == models.JobTypeOnlineInference {
err = UpdateAITaskFromRemote(cloudbrain, c.QueryNoteBook)
res, err = c.QueryNoteBook(entity.JobIdAndVersionId{JobID: cloudbrain.JobID, VersionID: cloudbrain.VersionID})
} else {
err = UpdateAITaskFromRemote(cloudbrain, c.QueryTrainJob)
res, err = c.QueryTrainJob(entity.JobIdAndVersionId{JobID: cloudbrain.JobID, VersionID: cloudbrain.VersionID})
}
log.Info("remoteQueryFunc task.DisplayJobName = %s res = %+v ", cloudbrain.DisplayJobName, res)
if err != nil {
log.Error("query from remote err.cloudbrainID = %d err=%v", cloudbrain.ID, err)
return response.NewBizError(err)
}
if res == nil || res.JobId == "" {
log.Error("query from remote failed,response is empty,cloudbrainID = %d ", cloudbrain.ID)
return response.NewBizError(errors.New("response is empty"))
}

if strings.ToUpper(res.Status) == string(models.JobRunning) && strings.ToUpper(cloudbrain.Status) == string(models.JobWaiting) {
if !g.CheckDebugAddress(cloudbrain) {
log.Error("check debug address failed.cloudbrain.id = %d", cloudbrain.ID)
return nil
}
}

err = UpdateByQueryResponse(res, cloudbrain)
if err != nil {
log.Error("UpdateAITaskFromRemote err.cloudbrainId=%d err=%v", cloudbrainId, err)
log.Error("UpdateByQueryResponse err.cloudbrainId=%d err=%v", cloudbrainId, err)
return response.NewBizError(err)
}
log.Info("updateTask success.cloudbrainId=%d", cloudbrainId)


+ 155
- 2
services/ai_task_service/task/task_list.go View File

@@ -8,7 +8,7 @@ import (
"code.gitea.io/gitea/routers/response"
)

func GetAITaskList(req entity.GetTaskListReq) (*entity.AITaskListRes, *response.BizError) {
func GetRepoAITaskList(req entity.GetTaskListReq) (*entity.AITaskListRes, *response.BizError) {
page := req.Page
if page <= 0 {
page = 1
@@ -34,7 +34,7 @@ func GetAITaskList(req entity.GetTaskListReq) (*entity.AITaskListRes, *response.

for i := 0; i < len(tasks); i++ {
r[i] = &entity.AITaskInfo4List{
Task: entity.ConvertCloudbrainToAITaskBriefInfo(&tasks[i].Cloudbrain),
Task: entity.ConvertCloudbrainToAITaskBriefInfo(&tasks[i].Cloudbrain).ClearNonPublicFields(),
Creator: *entity.ConvertUserToBrief(&tasks[i].User),
CanModify: tasks[i].CanUserModify(req.Operator),
CanDelete: tasks[i].CanUserDelete(req.Operator, req.IsRepoOwner),
@@ -48,3 +48,156 @@ func GetAITaskList(req entity.GetTaskListReq) (*entity.AITaskListRes, *response.
Page: page,
}, nil
}

func GetMyAITaskList(req entity.GetMyTaskListReq) (*entity.AITaskListRes, *response.BizError) {
var jobTypes []string
jobTypeNot := false
if req.JobType == string(models.JobTypeBenchmark) {
jobTypes = models.AllBenchMarkJobType()
} else if req.JobType != "all" && req.JobType != "" {
jobTypes = append(jobTypes, req.JobType)
}

var jobStatuses []string
jobStatusNot := false
if req.JobStatus == "other" {
jobStatusNot = true
jobStatuses = append(jobStatuses, req.ExcludeStatus...)
} else if req.JobStatus != "all" && req.JobStatus != "" {
jobStatuses = append(jobStatuses, req.JobStatus)
}

repos, _, err := models.SearchRepository(&models.SearchRepoOptions{
Actor: req.User,
OwnerID: req.User.ID,
Private: true,
})
if err != nil {
return nil, response.NewBizError(err)
}
var repoIDList []int64
for i, _ := range repos {
repoIDList = append(repoIDList, repos[i].ID)
}
computeSourceName := ""
if req.ComputeSource != nil {
computeSourceName = req.ComputeSource.GetCloudbrainFormat()
}
tasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: req.Page,
PageSize: req.PageSize,
},
Keyword: req.Keyword,
UserID: req.User.ID,
JobTypeNot: jobTypeNot,
JobStatusNot: jobStatusNot,
JobStatus: jobStatuses,
JobTypes: jobTypes,
NeedRepoInfo: true,
IsLatestVersion: modelarts.IsLatestVersion,
RepoIDList: repoIDList,
ComputeResource: computeSourceName,
Type: models.TypeCloudBrainAll,
AiCenter: req.AICenter,
Cluster: req.Cluster,
})
if err != nil {
log.Error("GetMyAITaskList query cloudbrains err. req=%+v,err=%v", req, err)
return nil, response.NewBizError(err)
}
models.LoadSpecs4CloudbrainInfo(tasks)
r := make([]*entity.AITaskInfo4List, len(tasks))

for i := 0; i < len(tasks); i++ {

r[i] = &entity.AITaskInfo4List{
Task: entity.ConvertCloudbrainToAITaskBriefInfo(&tasks[i].Cloudbrain).ClearNonPublicFields(),
CanModify: tasks[i].CanUserModify(req.User),
CanDelete: tasks[i].CanUserDelete(req.User, req.IsRepoOwner),
RepoName: tasks[i].Repo.Name,
OwnerName: tasks[i].Repo.OwnerName,
}
}

return &entity.AITaskListRes{
Tasks: r,
Total: count,
PageSize: req.PageSize,
Page: req.Page,
}, nil
}

func GetAITaskList4Admin(req entity.GetMyTaskListReq) (*entity.AITaskListRes, *response.BizError) {
var jobTypes []string
jobTypeNot := false
if req.JobType == string(models.JobTypeBenchmark) {
jobTypes = models.AllBenchMarkJobType()
} else if req.JobType != "all" && req.JobType != "" {
jobTypes = append(jobTypes, req.JobType)
}

var jobStatuses []string
jobStatusNot := false
if req.JobStatus == "other" {
jobStatusNot = true
jobStatuses = append(jobStatuses, req.ExcludeStatus...)
} else if req.JobStatus != "all" && req.JobStatus != "" {
jobStatuses = append(jobStatuses, req.JobStatus)
}

computeSourceName := ""
if req.ComputeSource != nil {
computeSourceName = req.ComputeSource.GetCloudbrainFormat()
}
tasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: req.Page,
PageSize: req.PageSize,
},
Keyword: req.Keyword,
JobTypeNot: jobTypeNot,
JobStatusNot: jobStatusNot,
JobStatus: jobStatuses,
JobTypes: jobTypes,
NeedRepoInfo: true,
IsLatestVersion: modelarts.IsLatestVersion,
ComputeResource: computeSourceName,
Type: models.TypeCloudBrainAll,
AiCenter: req.AICenter,
Cluster: req.Cluster,
})
if err != nil {
log.Error("GetMyAITaskList query cloudbrains err. req=%+v,err=%v", req, err)
return nil, response.NewBizError(err)
}
models.LoadSpecs4CloudbrainInfo(tasks)
r := make([]*entity.AITaskInfo4List, len(tasks))

for i := 0; i < len(tasks); i++ {
var repoName, ownerName string
if tasks[i].Repo != nil {
repoName = tasks[i].Repo.Name
ownerName = tasks[i].Repo.OwnerName
}
creator := entity.UserBriefInfo{}
if tasks[i].User.ID > 0 {
creator = *entity.ConvertUserToBrief(&tasks[i].User)
}
r[i] = &entity.AITaskInfo4List{
Task: entity.ConvertCloudbrainToAITaskBriefInfo(&tasks[i].Cloudbrain),
Creator: creator,
CanModify: true,
CanDelete: true,
RepoName: repoName,
OwnerName: ownerName,
}
}

return &entity.AITaskListRes{
Tasks: r,
Total: count,
PageSize: req.PageSize,
Page: req.Page,
}, nil
}

+ 46
- 21
services/ai_task_service/task/task_service.go View File

@@ -7,6 +7,8 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/redis/redis_key"
"code.gitea.io/gitea/modules/redis/redis_lock"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/routers/response"
@@ -24,6 +26,7 @@ import (
"path"
"strconv"
"strings"
"time"
)

type QueryFunc func(opts entity.JobIdAndVersionId) (*entity.QueryTaskResponse, error)
@@ -212,28 +215,10 @@ func QueryTaskBriefInfo(id int64) (*entity.AITaskBriefInfo, error) {
log.Error("QueryTaskBriefInfo GetCloudbrainByCloudbrainID err.id=%d err=%v", id, err)
return nil, err
}
task.LoadSpec()
return entity.ConvertCloudbrainToAITaskBriefInfo(task), nil
}

func UpdateAITaskFromRemote(task *models.Cloudbrain, remoteFunc QueryFunc) error {
log.Info("start to UpdateAITaskFromRemote.task.DisplayJobName = %s task.Status = %s", task.DisplayJobName, task.Status)
res, err := remoteFunc(entity.JobIdAndVersionId{JobID: task.JobID, VersionID: task.VersionID})
log.Info("remoteQueryFunc task.DisplayJobName = %s res = %+v ", task.DisplayJobName, res)
if err != nil {
log.Error("query from remote err.cloudbrainID = %d err=%v", task.ID, err)
return err
}
if res == nil {
log.Error("query from remote failed,response is empty,cloudbrainID = %d ", task.ID)
return errors.New("response is empty")
}
if res.JobId == "" {
log.Error("query from remote failed,response jobId is empty,cloudbrainID = %d ", task.ID)
return nil
}
return UpdateByQueryResponse(res, task)
}

func StopAITaskByJobNameFromRemote(task *models.Cloudbrain, queryFunc QueryListFunc, stopFunc StopFunc) error {
if task.IsTerminal() {
return nil
@@ -508,13 +493,16 @@ func QueryNoteBookUrl(id int64, getNoteBookUrl GetNotebookUrlFunc, fileName stri
if err != nil {
return "", err
}
if !cloudbrain.IsRunning() {
return "", errors.New("AI task is not running")
if cloudbrain.JobID == "" {
return "", errors.New("JobID is empty")
}
url, err := getNoteBookUrl(cloudbrain.JobID)
if err != nil {
return "", err
}
if url == "" {
return "", nil
}
if fileName != "" {
url = transferFileNotebookUrl(url, fileName)
}
@@ -897,3 +885,40 @@ func ClearNotebook() {
}

}

func TryToNotifyLongRunningTask(cloudbrain *models.Cloudbrain) {
startTimestamp := int64(cloudbrain.StartTime)
if setting.CloudbrainLongRunningNotifyInterval == 0 || startTimestamp == 0 {
return
}
currentTimestamp := time.Now().Unix()
matchIndex := 0
i := 1
for {
duration := time.Duration(i) * setting.CloudbrainLongRunningNotifyInterval
s := int64(duration.Seconds())
if currentTimestamp-startTimestamp < s {
matchIndex = i - 1
break
}
i++
}
if matchIndex <= 0 {
return
}
d := time.Duration(matchIndex) * setting.CloudbrainLongRunningNotifyInterval
if hasLongRunningNotificationSendBefore(cloudbrain, d) {
return
}
notification.NotifyLongRunningAITask(cloudbrain, d)
}

func hasLongRunningNotificationSendBefore(cloudbrain *models.Cloudbrain, duration time.Duration) bool {
key := redis_key.LongRunningNotificationKey(cloudbrain, duration)
lock := redis_lock.NewDistributeLock(key)
success, err := lock.Lock(duration + 1*time.Hour)
if err != nil {
return true
}
return !success
}

+ 41
- 29
services/card_request/card_request.go View File

@@ -45,20 +45,24 @@ func DisagreeRequest(cardReq api.CardReq) error {

func UpdateCardRequestAdmin(cardReq api.CardReq) error {
request := models.CardRequest{
ID: cardReq.ID,
ComputeResource: cardReq.ComputeResource,
CardType: cardReq.CardType,
AccCardsNum: cardReq.AccCardsNum,
EmailAddress: cardReq.EmailAddress,
DiskCapacity: cardReq.DiskCapacity,
Contact: cardReq.Contact,
PhoneNumber: cardReq.PhoneNumber,
Wechat: cardReq.Wechat,
BeginDate: cardReq.BeginDate,
EndDate: cardReq.EndDate,
Description: cardReq.Description,
Org: cardReq.Org,
ResourceType: cardReq.ResourceType,
ID: cardReq.ID,
ComputeResource: cardReq.ComputeResource,
CardType: cardReq.CardType,
AccCardsNum: cardReq.AccCardsNum,
EmailAddress: cardReq.EmailAddress,
DiskCapacity: cardReq.DiskCapacity,
Contact: cardReq.Contact,
PhoneNumber: cardReq.PhoneNumber,
Wechat: cardReq.Wechat,
IsResearchProject: cardReq.IsResearchProject,
InstitutionName: cardReq.InstitutionName,
ProjectName: cardReq.ProjectName,
ProjectCode: cardReq.ProjectCode,
BeginDate: cardReq.BeginDate,
EndDate: cardReq.EndDate,
Description: cardReq.Description,
Org: cardReq.Org,
ResourceType: cardReq.ResourceType,
}
beginTime, err := time.Parse(DATE_LAYOUT, cardReq.BeginDate)
if err != nil {
@@ -93,6 +97,10 @@ func UpdateCardRequest(cardReq api.CardReq, request *models.CardRequest) error {
request.EmailAddress = cardReq.EmailAddress
request.PhoneNumber = cardReq.PhoneNumber
request.Wechat = cardReq.Wechat
request.IsResearchProject = cardReq.IsResearchProject
request.InstitutionName = cardReq.InstitutionName
request.ProjectName = cardReq.ProjectName
request.ProjectCode = cardReq.ProjectCode

beginTime, err := time.Parse(DATE_LAYOUT, cardReq.BeginDate)
if err != nil {
@@ -115,21 +123,25 @@ func UpdateCardRequest(cardReq api.CardReq, request *models.CardRequest) error {
func CreateCardRequest(cardReq api.CardReq, uid int64) error {

bean := &models.CardRequest{
UID: uid,
ComputeResource: cardReq.ComputeResource,
CardType: cardReq.CardType,
AccCardsNum: cardReq.AccCardsNum,
EmailAddress: cardReq.EmailAddress,
DiskCapacity: cardReq.DiskCapacity,
Contact: cardReq.Contact,
PhoneNumber: cardReq.PhoneNumber,
Wechat: cardReq.Wechat,
BeginDate: cardReq.BeginDate,
EndDate: cardReq.EndDate,
Description: cardReq.Description,
Org: cardReq.Org,
ResourceType: cardReq.ResourceType,
Status: models.CARD_REQUEST_COMMIT,
UID: uid,
ComputeResource: cardReq.ComputeResource,
CardType: cardReq.CardType,
AccCardsNum: cardReq.AccCardsNum,
EmailAddress: cardReq.EmailAddress,
DiskCapacity: cardReq.DiskCapacity,
Contact: cardReq.Contact,
PhoneNumber: cardReq.PhoneNumber,
Wechat: cardReq.Wechat,
IsResearchProject: cardReq.IsResearchProject,
InstitutionName: cardReq.InstitutionName,
ProjectName: cardReq.ProjectName,
ProjectCode: cardReq.ProjectCode,
BeginDate: cardReq.BeginDate,
EndDate: cardReq.EndDate,
Description: cardReq.Description,
Org: cardReq.Org,
ResourceType: cardReq.ResourceType,
Status: models.CARD_REQUEST_COMMIT,
}

beginTime, err := time.Parse(DATE_LAYOUT, cardReq.BeginDate)


+ 3
- 1
services/cloudbrain/resource/resource_queue.go View File

@@ -60,7 +60,9 @@ func filterGrampusImage(all []models.GrampusImage) []models.GrampusImage {
result := make([]models.GrampusImage, 0)
for _, tmp := range all {
computeResource := getComputeResourceByProcessType(tmp.ProcessorType)
if computeResource == "GCU" || computeResource == "MLU" || computeResource == "ILUVATAR-GPGPU" || computeResource == "METAX-GPGPU" {
if computeResource == "GCU" || computeResource == "MLU" ||
computeResource == "ILUVATAR-GPGPU" || computeResource == "METAX-GPGPU" ||
computeResource == "GPU" {
result = append(result, tmp)
}
}


+ 1
- 1
services/socketwrap/clientManager.go View File

@@ -10,7 +10,7 @@ import (
"github.com/elliotchance/orderedmap"
)

var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 53}
var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 53, 54}

type ClientsManager struct {
Clients *orderedmap.OrderedMap


+ 4
- 573
templates/admin/cloudbrain/list.tmpl View File

@@ -1,579 +1,10 @@
{{template "base/head" .}}
<script src="{{StaticUrlPrefix}}/js/specsuse.js?v={{MD5 AppVer}}" type="text/javascript"></script>
<!-- 弹窗 -->
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<!-- 提示框 -->
<div class="alert"></div>
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-cloudbrain-tasks.css?v={{MD5 AppVer}}" />
<div class="admin user">
<div class="cloudbrain_debug" style="display: none;" data-debug="{{$.i18n.Tr "repo.debug"}}"
data-debug-again="{{$.i18n.Tr "repo.debug_again"}}"
data-all-cluster="{{.i18n.Tr "cloudbrain.all_resource_cluster"}}"
data-all-aiCenter="{{.i18n.Tr "cloudbrain.all_ai_center"}}"
data-cluster-c2net="{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}"
data-cluster-openi="{{.i18n.Tr "cloudbrain.resource_cluster_openi"}}"
data-all-task="{{.i18n.Tr "admin.cloudbrain.all_task_types"}}"
data-all-compute="{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}"
data-all-status="{{.i18n.Tr "admin.cloudbrain.all_status"}}"></div>
{{template "admin/navbar" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="ui grid" style="margin:0">
<div class="row" style="border: 1px solid #d4d4d5;margin-top: 0px;padding-top: 0;">
{{template "admin/cloudbrain/search" .}}
<div class="ui six wide column right aligned" style="margin: 1rem 0;">
<a class="ui compact blue basic icon button" style="box-shadow: none !important; padding: 0.8em;"
href="/admin/cloudbrains/download"><i
class="ri-download-line middle aligned icon"></i>{{.i18n.Tr "admin.cloudbrain.download_report"}}</a>
</div>
<div class="ui sixteen wide column" style="overflow-x:auto;">
<!-- 任务展示 -->
<div class="dataset list" style="min-width:2100px;margin-top:15px;margin-bottom:15px;">
<!-- 表头 -->
<div class="ui grid stackable" style="background: #f0f0f0;;">
<div class="row">
<div class="two wide column nowrap" style="width:10% !important;">
<span>{{$.i18n.Tr "repo.cloudbrain_task"}}</span>
</div>
<!-- 集群 -->
<div class="one wide column text center nowrap" style="width:6% !important;">
<span>{{$.i18n.Tr "repo.modelarts.cluster"}}</span>
</div>
<div class="two wide column text center nowrap" style="width: 8% !important;">
<span>{{$.i18n.Tr "repo.modelarts.status"}}</span>
</div>
<div class="one wide column text center nowrap" style="width:6% !important;">
<span>{{$.i18n.Tr "repo.cloudbrain_task_type"}}</span>
</div>
<div class="two wide column text center nowrap" style="width: 8% !important;">
<span>{{$.i18n.Tr "repo.modelarts.createtime"}}</span>
</div>
<div class="one wide column text center nowrap" style="width: 5% !important;">
<span>{{$.i18n.Tr "repo.cloudbrain_status_runtime"}}</span>
</div>
<div class="one wide column text center nowrap" style="width: 5% !important;">
<span>{{$.i18n.Tr "repo.modelarts.computing_resources"}}</span>
</div>
<!-- 智算中心 -->
<div class="one wide column text center nowrap" style="width:7% !important;">
<span>{{$.i18n.Tr "repo.modelarts.ai_center"}}</span>
</div>
<!-- XPU类型 -->
<div class="one wide column text center nowrap" style="width:7% !important;">
<span>{{$.i18n.Tr "repo.modelarts.card_type"}}</span>
</div>
<div class="one wide column text center nowrap" style="width:4% !important;">
<span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span>
</div>
<div class="two wide column text center nowrap" style="width:10% !important;">
<span>{{$.i18n.Tr "repository"}}</span>
</div>
<div class="two wide column text center nowrap" style="width:10% !important;">
<span>{{.i18n.Tr "admin.cloudbrain.cloudbrain_name"}}</span>
</div>
<div class="two wide column text center nowrap" style="width: 12%!important;">
<span>{{$.i18n.Tr "repo.cloudbrain_operate"}}</span>
</div>
</div>
</div>
{{range .Tasks}}
{{if .Repo}}
<div class="ui grid stackable item">
<div class="row">
<!-- 任务名 -->
{{$JobID := '0'}}
{{if eq .JobType "DEBUG" "TRAIN" "INFERENCE" "HPC" "SNN4IMAGENET" "BRAINSCORE" "BENCHMARK" "MODELSAFETY" "SNN4ECOSET" "SIM2BRAIN_SNN" "ONLINEINFERENCE"}}
{{$JobID = .Cloudbrain.ID}}
{{else}}
{{$JobID = .JobID}}
{{end}}
<!-- {{$JobID}} -->
<div class="two wide column nowrap" style="width:10% !important;">
{{if eq .JobType "DEBUG"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "HPC"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/supercompute/job/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE") (eq .JobType "SNN4ECOSET") (eq .JobType "SIM2BRAIN_SNN")}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/cloudbrain/benchmark/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "INFERENCE"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .Cloudbrain.Type 2}}grampus{{else if eq .Cloudbrain.Type 1}}modelarts{{else if eq .Cloudbrain.Type 0}}cloudbrain{{end}}/inference-job/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "TRAIN"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 0}}/cloudbrain{{else if eq .Cloudbrain.Type 1}}/modelarts{{else if eq .Cloudbrain.Type 2}}/grampus{{end}}/train-job/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/cloudbrain/benchmark/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "ONLINEINFERENCE"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/grampus/onlineinfer/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{end}}
</div>
<!-- 集群 -->
<div class="one wide column text center nowrap" style="width:6% !important;">
<span style="font-size: 12px;" class="cluster_{{.DisplayJobName}}_{{$JobID}}">{{if .Cluster}}{{.Cluster}}{{else}}--{{end}}</span>
</div>
<!-- 任务状态 -->
<div class="two wide column text center nowrap"
style="width: 8% !important;">
<span class="job-status" id="{{$JobID}}"
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG" "ONLINEINFERENCE" "HPC"}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}{{else if eq .JobType "INFERENCE"}}/modelarts/train-job{{else if eq .JobType "TRAIN"}}/modelarts/train-job{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}/cloudbrain{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"
data-cloudbrainid="{{.Cloudbrain.ID}}" data-datamigrate='{{$.i18n.Tr "repo.migratingData"}}' data-centerpend='{{$.i18n.Tr "repo.centerPending"}}'>
<span>
<i id="{{$JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i>
<span id="{{$JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span>
{{if eq .Status "WAITING"}}<i id="{{$JobID}}-icon-detail" class="{{.DetailedStatus}}" style="vertical-align: middle;" title='{{$.i18n.Tr (printf "repo.%s" .DetailedStatus)}}'></i>{{end}}
</span>
</span>
</div>
<!-- 任务类型 -->
<div class="one wide column text center nowrap" style="width: 6% !important;">
<span style="font-size: 12px;">{{.JobType}} </span>
</div>
<!-- 任务创建时间 -->
<div class="two wide column text center nowrap" style="width: 8% !important;">
<span style="font-size: 12px;"
class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span>
</div>
<!-- 任务运行时间 -->
<div class="one wide column text center nowrap" style="width: 5% !important;">
<span style="font-size: 12px;"
id="duration-{{$JobID}}">{{if .TrainJobDuration}}{{.TrainJobDuration}}{{else}}--{{end}}</span>
</div>
<!-- 计算资源 -->
<div class="one wide column text center nowrap" style="width: 5% !important;">
<span
style="font-size: 12px;" title="{{.ComputeResource}}">{{if .ComputeResource}}{{.ComputeResource}}{{else}}--{{end}}</span>
</div>
<!-- 智算中心 -->
<div class="one wide column text center nowrap" style="width:7% !important;">
<span style="font-size: 12px;" id="cluster-{{$JobID}}" class="aicenter_{{.DisplayJobName}}_{{$JobID}}" title="{{if .AiCenter}}{{.AiCenter}}{{else}}--{{end}}">{{if .AiCenter}}{{.AiCenter}}{{else}}--{{end}}</span>
</div>
<!-- XPU类型 -->
<div class="one wide column text center nowrap" style="width:7% !important;">
<span style="font-size: 12px;" title="" class="card_type_{{.DisplayJobName}}_{{$JobID}}"></span>
</div>
<script>
(function(){
var spec = {{.Spec}} || {};
var cardType = getListValueWithKey(ACC_CARD_TYPE, spec.AccCardType) || '--';
var spanEl = document.querySelector('.card_type_{{.DisplayJobName}}_{{$JobID}}');
spanEl.setAttribute('title', cardType);
spanEl.innerText = cardType;
var cluster = {{.Cluster}} || '--';
var clusterName = document.querySelector('.cloudbrain_debug').dataset['cluster' + cluster[0] + cluster.toLocaleLowerCase().slice(1)] || '--';
spanEl = document.querySelector('.cluster_{{.DisplayJobName}}_{{$JobID}}');
spanEl.setAttribute('title', cluster);
spanEl.innerText = clusterName;

// var aiCenter = spec.AiCenterName || '--';
// spanEl = document.querySelector('.aicenter_{{.DisplayJobName}}_{{$JobID}}');
// spanEl.setAttribute('title', aiCenter);
// spanEl.innerText = aiCenter;
})();
</script>
<!-- 创建者 -->
<div class="one wide column text center nowrap" style="width:4% !important;">
{{if .User.Name}}
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img
class="ui avatar image" src="{{.User.RelAvatarLink}}"></a>
{{else}}
<a title="Ghost"><img class="ui avatar image"
src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a>
{{end}}
</div>
<!-- 项目 -->
<div class="two wide column text center nowrap" style="width:10% !important;">
<a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}"
title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a>
</div>
<!-- 云脑侧名称 -->
<div class="two wide column text center nowrap"
style="overflow: hidden;text-overflow:ellipsis;width:10% !important;">
<span class="ui poping up clipboard" data-position="top center" id="clipboard-btn-{{.JobName}}" style="cursor:pointer"
data-clipboard-text="{{.JobName}}"
data-original="{{$.i18n.Tr "repo.copy"}}"
data-success="{{$.i18n.Tr "repo.copied"}}"
data-error="{{$.i18n.Tr "repo.copied_error"}}"
data-content="{{$.i18n.Tr "repo.copy"}}"
data-variation="inverted tiny"
>
<span class="fitted" title="{{.JobName}}">{{.JobName}}</span>
</span>
</div>
<div class="two wide column text center nowrap" style="width: 14%!important;">
{{if eq .JobType "DEBUG"}}
<div class="ui compact buttons">
<form id="debugAgainForm-{{$JobID}}">
{{$.CsrfTokenHtml}}
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}}
<a style="margin: 0 1rem;" id="ai-debug-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING" "CREATED_FAILED"}}disabled {{else}}blue {{end}}button'
data-jobid="{{$JobID}}"
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/'>
{{$.i18n.Tr "repo.debug"}}
</a>
{{else}}
{{if not .BootFile}}
<a id="ai-debug-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING" "CREATED_FAILED"}} disabled {{else}}blue {{end}}button'
data-jobid="{{$JobID}}"
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/'>
{{$.i18n.Tr "repo.debug_again"}}
</a>
{{end}}
{{end}}
</form>
</div>
{{end}}
{{if eq .JobType "ONLINEINFERENCE"}}
<div class="ui compact buttons">
<form id="debugAgainForm-{{$JobID}}">
{{$.CsrfTokenHtml}}
<a id="ai-debug-infer-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "RUNNING"}} blue {{else}} disabled {{end}}button'
data-jobid="{{$JobID}}"
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/'>
{{$.i18n.Tr "repo.online_debug"}}
</a>
</form>
</div>
{{end}}
{{if eq .JobType "HPC"}}
<div class="ui compact buttons">
<form id="debugAgainForm-{{$JobID}}">
{{$.CsrfTokenHtml}}
<a id="ai-debug-infer-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "RUNNING"}} blue {{else}} disabled {{end}}button'
data-jobid="{{$JobID}}"
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/'>
{{$.i18n.Tr "repo.start_use"}}
</a>
</form>
</div>
{{end}}
<!-- 停止任务 -->
<div class="ui compact buttons">
{{if eq .JobType "MODELSAFETY"}}
<form id="stopForm-{{$JobID}}" style="margin-left:-1px;">
{{$.CsrfTokenHtml}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop {{if eq .Status "RUNNING" "WAITING"}} blue {{else}} disabled {{end}} button'
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/modelsafety/{{$JobID}}/stop'
data-jobid="{{$JobID}}">
{{$.i18n.Tr "repo.stop"}}
</a>
</form>
{{else}}
{{if eq .JobType "DEBUG" "HPC" "BENCHMARK" "SNN4IMAGENET" "BRAINSCORE" "SNN4ECOSET" "SIM2BRAIN_SNN" "ONLINEINFERENCE"}}
<form id="stopForm-{{$JobID}}" style="margin-left:-1px;">
{{$.CsrfTokenHtml}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop {{if eq .Status "RUNNING" "WAITING"}} blue {{else}} disabled {{end}} button'
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else if eq .JobType "BENCHMARK" }}/cloudbrain/benchmark{{else if eq .ComputeResource "NPU" }}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/stop'
data-jobid="{{$JobID}}" data-bootfile="{{.BootFile}}">
{{$.i18n.Tr "repo.stop"}}
</a>
</form>
{{else}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class="ui basic ai_stop_version {{if eq .Status "RUNNING" "WAITING"}} blue {{else}} disabled {{end}} button"
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 1}}modelarts/inference-job{{else}}cloudbrain/train-job{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}"
data-jobid="{{$JobID}}"
data-cloudbrainid="{{.Cloudbrain.ID}}"
data-version="{{.VersionName}}">
{{$.i18n.Tr "repo.stop"}}
</a>
{{end}}
{{end}}
</div>
<!-- 修改任务 -->
{{if and (eq .JobType "TRAIN") (not .FineTune)}}
<div class="ui compact buttons __btn_edit__">
<a style="padding: 0.5rem 1rem;" class="ui basic blue button" href='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job/{{if eq .ComputeResource "CPU/GPU"}}gpu{{else}}{{ToLower .ComputeResource}}{{end}}{{end}}/create?modify=true&id={{$JobID}}'>
{{$.i18n.Tr "repo.modelarts.modify"}}
</a>
</div>
{{end}}
<!-- 删除任务 -->
{{if eq .JobType "MODELSAFETY"}}
<form class="ui compact buttons" id="delForm-{{$JobID}}"
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/modelsafety/{{$JobID}}/del?isadminpage=true'
method="post">
{{$.CsrfTokenHtml}}
<input type="hidden" value="{{.Cloudbrain.ID}}" style="display:none" name="id" />
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}"
class="ui basic ai_delete blue button"
style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
</form>
{{else}}
<form class="ui compact buttons" id="delForm-{{$JobID}}"
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE") (eq .JobType "SNN4ECOSET") (eq .JobType "SIM2BRAIN_SNN")}}/cloudbrain{{else if eq .JobType "DEBUG" "ONLINEINFERENCE" "HPC"}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{else if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{end}}/{{$JobID}}/del?isadminpage=true'
method="post">
{{$.CsrfTokenHtml}}
<input type="hidden" value="{{.Cloudbrain.ID}}" style="display:none" name="id" />
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}"
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}/modelarts/inference-job/{{$JobID}}/del_version?isadminpage=true"
data-version="{{.VersionName}}"
data-cloudbrainid="{{.Cloudbrain.ID}}"
class="ui basic ai_delete blue button"
style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
</form>
{{end}}
</div>
</div>
</div>
{{else}}
{{$JobID := '0'}}
{{if eq .JobType "DEBUG" "TRAIN" "SNN4IMAGENET" "BRAINSCORE" "BENCHMARK" "SNN4ECOSET" "SIM2BRAIN_SNN"}}
{{$JobID = .Cloudbrain.ID}}
{{else}}
{{$JobID = .JobID}}
{{end}}
<div class="ui grid stackable item">
<div class="row">
<!-- 任务名 -->
<div class="two wide column nowrap" style="width:10% !important;">
{{if eq .JobType "DEBUG"}}
<a class="title" href="" title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "INFERENCE"}}
<a class="title" href="" title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "TRAIN"}}
<a class="title" href="" title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "BENCHMARK"}}
<a class="title" href="" title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{end}}
</div>
<!-- 集群 -->
<div class="one wide column text center nowrap" style="width:6% !important;">
<span
style="font-size: 12px;">{{if .Cluster}}{{.Cluster}}{{else}}--{{end}}</span>
</div>
<!-- 任务状态 -->
<div class="two wide column text center nowrap"
style="width: 8% !important;">
<span class="job-status" id="{{$JobID}}"
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG" "ONLINEINFERENCE" "HPC"}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}{{else if eq .JobType "INFERENCE"}}/modelarts/inference-job{{else if eq .JobType "TRAIN"}}/modelarts/train-job{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}/cloudbrain{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"
data-cloudbrainid="{{.Cloudbrain.ID}}" data-datamigrate='{{$.i18n.Tr "repo.migratingData"}}' data-centerpend='{{$.i18n.Tr "repo.centerPending"}}'>
<span>
<i id="{{$JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i>
<span id="{{$JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span>
{{if eq .Status "WAITING"}}<i id="{{$JobID}}-icon-detail" class="{{.DetailedStatus}}" style="vertical-align: middle;" title='{{$.i18n.Tr (printf "repo.%s" .DetailedStatus)}}'></i>{{end}}
</span>
</span>
</div>
<!-- 任务类型 -->
<div class="one wide column text center nowrap" style="width:6% !important;">
<span style="font-size: 12px;">{{.JobType}} </span>
</div>
<!-- 任务创建时间 -->
<div class="two wide column text center nowrap" style="width: 8% !important;">
<span style="font-size: 12px;"
class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span>
</div>
<!-- 任务运行时间 -->
<div class="one wide column text center nowrap" style="width:5% !important;">
<span style="font-size: 12px;"
id="duration-{{$JobID}}">{{if .TrainJobDuration}}{{.TrainJobDuration}}{{else}}--{{end}}</span>
</div>
<!-- 计算资源 -->
<div class="one wide column text center nowrap" style="width:5% !important;">
<span
style="font-size: 12px;" title="{{.ComputeResource}}">{{if .ComputeResource}}{{.ComputeResource}}{{else}}--{{end}}</span>
</div>
<!-- 智算中心 -->
<div class="one wide column text center nowrap" style="width:7% !important;">
<span
style="font-size: 12px;">{{if .AiCenter}}{{.AiCenter}}{{else}}--{{end}}</span>
</div>
<!-- XPU类型 -->
<div class="one wide column text center nowrap" style="width:7% !important;">
<span style="font-size: 12px;" title="{{.CardType}}">
{{if .CardType}}{{.CardType}}{{else}}--{{end}}
</span>
</div>
<!-- 创建者 -->
<div class="one wide column text center nowrap" style="width:4% !important;">
{{if .User.Name}}
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img
class="ui avatar image" src="{{.User.RelAvatarLink}}"></a>
{{else}}
<a title="Ghost"><img class="ui avatar image"
src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a>
{{end}}
</div>
<!-- 项目 -->
<div class="two wide column text center nowrap" style="width:10% !important;">
<a href="" title="">--</a>
</div>
<!-- 云脑侧名称 -->
<div class="two wide column text center nowrap"
style="overflow: hidden;text-overflow:ellipsis;width:10% !important;">
<span class="ui poping up clipboard" data-position="top center" id="clipboard-btn" style="cursor:pointer"
data-clipboard-text="{{.JobName}}"
data-success="{{$.i18n.Tr "repo.copy_link_success"}}"
data-error="{{$.i18n.Tr "repo.copy_link_error"}}"
data-content="{{$.i18n.Tr "repo.copy_link"}}"
data-variation="inverted tiny"
>
<span class="fitted" title="{{.JobName}}">{{.JobName}}</span>
</span>
</div>
<div class="two wide column text center nowrap" style="width: 14%!important;">
{{if eq .JobType "DEBUG"}}
<div class="ui compact buttons">
<form id="debugAgainForm-{{$JobID}}">
{{$.CsrfTokenHtml}}
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}}
<a style="margin: 0 1rem;" id="ai-debug-{{$JobID}}"
class='ui basic disabled button'>
{{$.i18n.Tr "repo.debug"}}
</a>
{{else}}
<a id="ai-debug-{{$JobID}}" class='ui basic disabled button'>
{{$.i18n.Tr "repo.debug_again"}}
</a>
{{end}}
</form>
</div>
{{end}}
<!-- 停止任务 -->
<div class="ui compact buttons">
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class="ui basic disabled button" data-jobid="{{$JobID}}"
data-version="{{.VersionName}}">
{{$.i18n.Tr "repo.stop"}}
</a>
</div>
<!-- 删除任务 -->
<form class="ui compact buttons" id="delForm-{{$JobID}}" action='' method="post">
{{$.CsrfTokenHtml}}
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}"
class="ui basic disabled button" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
</form>
</div>
</div>
</div>

{{end}}
{{end}}
</div>
</div>
<div id="app" style="margin-top: 2rem;width:100%;">
<div class="center">
<el-pagination background @current-change="handleCurrentChange" :current-page="page"
:page-sizes="[10]" :page-size="10" layout="total, sizes, prev, pager, next, jumper"
:total="{{.Page.Paginater.Total}}">
</el-pagination>
</div>
</div>
</div>
</div>
</div>
<!-- 确认模态框 -->
<div id="deletemodel">
<div class="ui basic modal">
<div class="ui icon header">
<i class="trash icon"></i> {{.i18n.Tr "cloudbrain.delete_task"}}
</div>

<div class="content">
<p>{{.i18n.Tr "cloudbrain.task_delete_confirm"}}</p>
</div>
<div class="actions">
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i> {{.i18n.Tr "cloudbrain.operate_cancel"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i> {{.i18n.Tr "cloudbrain.operate_confirm"}}
</div>
</div>
</div>
</div>
<div class="ui modal debug-again-alert">
<div class="ui message" style="background-color: rgba(242, 113, 28, 0.05);border: 1px solid rgba(242, 113, 28, 1);border-radius: 5px;">
<div style="display: flex;align-items: center;">
<i class="ri-information-line" style="font-size: 35px;color: rgba(242, 113, 28, 1);;"></i>
<div style="text-align: left;margin-left: 1rem;">
<div style="font-weight: 600;line-height: 2;">{{.i18n.Tr "repo.cloudbrain.morethanonejob1" | Safe }}</div>
<div style="color:#939393">{{.i18n.Tr "repo.cloudbrain.morethanonejob2" | Safe}}</div>
</div>
</div>
</div>
<div id="__vue-root"></div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
var editbtns = $('.__btn_edit__');
var curHref = window.location.href;
for (var i = 0, iLen = editbtns.length; i < iLen; i++) {
var buttonEl = editbtns.eq(i).find('a');
var oHref = buttonEl.attr('href');
var hasSearch = oHref.split('?').length > 1;
buttonEl.attr('href', oHref + (hasSearch ? '&' : '?') + 'backurl=' + encodeURIComponent(curHref));
}
});
</script>
{{template "base/footer" .}}
<script src="{{StaticUrlPrefix}}/js/vp-cloudbrain-tasks.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 1
- 0
templates/repo/cloudbrain/benchmark/index.tmpl View File

@@ -34,6 +34,7 @@
<a class="item" href="{{.RepoLink}}/modelarts/inference-job">{{$.i18n.Tr "repo.modelarts.infer_job"}}</a>
<a class="active item" href="{{.RepoLink}}/cloudbrain/benchmark">{{$.i18n.Tr "repo.modelarts.evaluate_job"}}</a>
<a class="item" href="{{.RepoLink}}/grampus/onlineinfer">{{$.i18n.Tr "repo.modelarts.online_infer"}}</a>
<a class="item" href="{{.RepoLink}}/grampus/general">{{$.i18n.Tr "repo.modelarts.general_job"}}</a>
{{if MLOPS}}
<a class="item" href="{{MlopsHost}}/AIStudio/mlops/deploy-online/edge-inference?reponame={{.Repository.Name}}&repoId={{.Repository.ID}}" target="_blank">
在线推理1


+ 8
- 0
templates/repo/grampus/general/list.tmpl View File

@@ -0,0 +1,8 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-cloudbrain-list.css?v={{MD5 AppVer}}" />
<div class="repository release dataset-list view">
{{template "repo/header" .}}
<div id="__vue-root"></div>
</div>
<script src="{{StaticUrlPrefix}}/js/vp-cloudbrain-list.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 1
- 0
templates/repo/grampus/general/new.tmpl View File

@@ -0,0 +1 @@
{{ template "repo/cloudbrain/cloudbraincreate" .}}

+ 1
- 0
templates/repo/grampus/general/show.tmpl View File

@@ -0,0 +1 @@
{{ template "repo/cloudbrain/cloudbraindetail" .}}

+ 3
- 525
templates/user/dashboard/cloudbrains.tmpl View File

@@ -1,529 +1,7 @@
{{template "base/head" .}}
<!-- 提示框 -->
<div class="alert"></div>
<script src="{{StaticUrlPrefix}}/js/specsuse.js?v={{MD5 AppVer}}" type="text/javascript"></script>
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-cloudbrain-tasks.css?v={{MD5 AppVer}}" />
<div class="explore users">
<div class="cloudbrain_debug" style="display: none;" data-debug="{{$.i18n.Tr "repo.debug"}}"
data-debug-again="{{$.i18n.Tr "repo.debug_again"}}" data-debug-task="{{$.i18n.Tr "cloudbrain.DEBUG"}}"
data-train-task="{{$.i18n.Tr "cloudbrain.TRAIN"}}" data-inference-task="{{$.i18n.Tr "cloudbrain.INFERENCE"}}"
data-benchmark-task="{{$.i18n.Tr "cloudbrain.BENCHMARK"}}" data-inferonline-task="{{$.i18n.Tr "cloudbrain.ONLINEINFERENCE"}}"
data-supercompute-task="{{$.i18n.Tr "repo.superComputeTask"}}"
data-all-cluster="{{.i18n.Tr "cloudbrain.all_resource_cluster"}}"
data-all-aiCenter="{{.i18n.Tr "cloudbrain.all_ai_center"}}"
data-cluster-c2net="{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}"
data-cluster-openi="{{.i18n.Tr "cloudbrain.resource_cluster_openi"}}"
data-all-task="{{.i18n.Tr "admin.cloudbrain.all_task_types"}}"
data-all-compute="{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}"
data-all-status="{{.i18n.Tr "admin.cloudbrain.all_status"}}"></div>
{{template "admin/cloudbrain/search_dashboard" .}}
<div class="ui container" style="width:90%;overflow-x:auto;overflow-y:hidden">
{{template "base/alert" .}}
<div class="ui grid" style="min-width:1700px;">
<div class="row">
<div class="ui sixteen wide column" style="margin-bottom:15px;">
<!-- 任务展示 -->
<div class="dataset list">
<!-- 表头 -->
<div class="ui grid stackable" style="background: #f0f0f0;;">
<div class="row">
<div class="three wide column nowrap" style="width:12%!important">
<span>{{$.i18n.Tr "repo.cloudbrain_task"}}</span>
</div>
<!-- 集群 -->
<div class="one wide column text center nowrap" style="width:8% !important;">
<span>{{$.i18n.Tr "repo.modelarts.cluster"}}</span>
</div>
<div class="two wide column text center nowrap" style="width: 8% !important;">
<span>{{$.i18n.Tr "repo.modelarts.status"}}</span>
</div>
<div class="one wide column text center nowrap" style="width:8% !important">
<span>{{$.i18n.Tr "repo.cloudbrain_task_type"}}</span>
</div>
<div class="two wide column text center nowrap" style="width: 8% !important;">
<span>{{$.i18n.Tr "repo.modelarts.createtime"}}</span>
</div>
<div class="one wide column text center nowrap" style="width:6% !important;">
<span>{{$.i18n.Tr "repo.cloudbrain_status_runtime"}}</span>
</div>
<div class="one wide column text center nowrap" style="width:6% !important;">
<span>{{$.i18n.Tr "repo.modelarts.computing_resources"}}</span>
</div>
<!-- 智算中心 -->
<div class="one wide column text center nowrap" style="width:8% !important;">
<span>{{$.i18n.Tr "repo.modelarts.ai_center"}}</span>
</div>
<!-- XPU类型 -->
<div class="one wide column text center nowrap" style="width:10% !important;">
<span>{{$.i18n.Tr "repo.modelarts.card_type"}}</span>
</div>
<div class="two wide column text center nowrap" style="width: 11%!important;">
<span>{{$.i18n.Tr "repository"}}</span>
</div>

<div class="three wide column text center nowrap" style="width: 15%!important;">
<span>{{$.i18n.Tr "repo.cloudbrain_operate"}}</span>
</div>
</div>
</div>
{{range .Tasks}}
{{if .Repo}}
<div class="ui grid stackable item">
<div class="row">
<!-- 任务名 -->
{{$JobID := '0'}}
{{if eq .JobType "DEBUG" "TRAIN" "INFERENCE" "HPC" "SNN4IMAGENET" "BRAINSCORE" "BENCHMARK" "MODELSAFETY" "SNN4ECOSET" "SIM2BRAIN_SNN" "ONLINEINFERENCE"}}
{{$JobID = .Cloudbrain.ID}}
{{else}}
{{$JobID = .JobID}}
{{end}}
<!-- {{$JobID}} -->
<div class="three wide column nowrap" style="width:12% !important">
{{if eq .JobType "DEBUG"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "HPC"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/supercompute/job/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if (eq .JobType "SNN4IMAGENET" "BRAINSCORE" "SNN4ECOSET" "SIM2BRAIN_SNN")}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/cloudbrain/benchmark/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "INFERENCE"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .Cloudbrain.Type 2}}grampus{{else if eq .Cloudbrain.Type 1}}modelarts{{else if eq .Cloudbrain.Type 0}}cloudbrain{{end}}/inference-job/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "TRAIN"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .Cloudbrain.Type 1}}modelarts{{else if eq .Cloudbrain.Type 0}}cloudbrain{{else if eq .Cloudbrain.Type 2}}grampus{{end}}/train-job/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/cloudbrain/benchmark/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "ONLINEINFERENCE"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/grampus/onlineinfer/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{end}}
</div>
<!-- 集群 -->
<div class="one wide column text center nowrap" style="width:8% !important;">
<span style="font-size: 12px;" class="cluster_{{.DisplayJobName}}_{{$JobID}}">{{if .Cluster}}{{.Cluster}}{{else}}--{{end}}</span>
</div>
<!-- 任务状态 -->
<div class="two wide column text center nowrap"
style="width: 8% !important;">
<span class="job-status" id="{{$JobID}}"
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG" "ONLINEINFERENCE" "HPC"}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}{{else if eq .JobType "INFERENCE"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts{{end}}/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}/cloudbrain{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"
data-cloudbrainid="{{.Cloudbrain.ID}}"
data-bootfile="{{.BootFile}}" data-datamigrate='{{$.i18n.Tr "repo.migratingData"}}' data-centerpend='{{$.i18n.Tr "repo.centerPending"}}'>
<span>
<i id="{{$JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i>
<span id="{{$JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span>
{{if eq .Status "WAITING"}}<i id="{{$JobID}}-icon-detail" class="{{.DetailedStatus}}" style="vertical-align: middle;" title='{{$.i18n.Tr (printf "repo.%s" .DetailedStatus)}}'></i>{{end}}
</span>
</span>
</div>
<!-- 任务类型 -->

{{$JobType := $.i18n.Tr (printf "cloudbrain.%s" .JobType)}}
<div class="one wide column text center nowrap" style="width:8% !important">
<span style="font-size: 12px;" title="{{.JobType}}">{{$JobType}}</span>
</div>
<!-- 任务创建时间 -->
<div class="two wide column text center nowrap" style="width: 8% !important;">
<span style="font-size: 12px;"
class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span>
</div>
<!-- 任务运行时间 -->
<div class="one wide column text center nowrap" style="width:6% !important;">
<span style="font-size: 12px;"
id="duration-{{$JobID}}">{{if .TrainJobDuration}}{{.TrainJobDuration}}{{else}}--{{end}}</span>
</div>
<!-- 计算资源 -->
<div class="one wide column text center nowrap" style="width:6% !important;">
<span
style="font-size: 12px;" title="{{.ComputeResource}}">{{if .ComputeResource}}{{.ComputeResource}}{{else}}--{{end}}</span>
</div>
<!-- 智算中心 -->
<div class="one wide column text center nowrap" style="width:8% !important;">
<span style="font-size: 12px;" id="cluster-{{$JobID}}" class="aicenter_{{.DisplayJobName}}_{{$JobID}}" title="{{if .AiCenter}}{{.AiCenter}}{{else}}--{{end}}">{{if .AiCenter}}{{.AiCenter}}{{else}}--{{end}}</span>
</div>
<!-- XPU类型 -->
<div class="one wide column text center nowrap" style="width:10% !important;">
<span style="font-size: 12px;" title="" class="card_type_{{.DisplayJobName}}_{{$JobID}}"></span>
</div>
<script>
(function(){
var spec = {{.Spec}} || {};
var cardType = getListValueWithKey(ACC_CARD_TYPE, spec.AccCardType) || '--';
var spanEl = document.querySelector('.card_type_{{.DisplayJobName}}_{{$JobID}}');
spanEl.setAttribute('title', cardType);
spanEl.innerText = cardType;
var cluster = {{.Cluster}} || '--';
var clusterName = document.querySelector('.cloudbrain_debug').dataset['cluster' + cluster[0] + cluster.toLocaleLowerCase().slice(1)] || '--';
spanEl = document.querySelector('.cluster_{{.DisplayJobName}}_{{$JobID}}');
spanEl.setAttribute('title', cluster);
spanEl.innerText = clusterName;

// var aiCenter = spec.AiCenterName || '--';
// spanEl = document.querySelector('.aicenter_{{.DisplayJobName}}_{{$JobID}}');
// spanEl.setAttribute('title', aiCenter);
// spanEl.innerText = aiCenter;
})();
</script>
<!-- 项目 -->
<div class="two wide column text center nowrap" style="width: 11%!important;">
<a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}"
title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a>
</div>
<div class="three wide column text center nowrap" style="width: 15%!important;">
{{if eq .JobType "DEBUG"}}
<div class="ui compact buttons">
<form id="debugAgainForm-{{$JobID}}">
{{$.CsrfTokenHtml}}
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}}
<a style="margin: 0 1rem;" id="ai-debug-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING" "CREATED_FAILED"}}disabled {{else}}blue {{end}}button'
data-jobid="{{$JobID}}"
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/'>
{{$.i18n.Tr "repo.debug"}}
</a>
{{else}}
{{if not .BootFile}}
<a id="ai-debug-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING" "CREATED_FAILED"}} disabled {{else}}blue {{end}}button'
data-jobid="{{$JobID}}"
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/'>
{{$.i18n.Tr "repo.debug_again"}}
</a>
{{end}}
{{end}}
</form>
</div>
{{end}}
{{if eq .JobType "ONLINEINFERENCE"}}
<div class="ui compact buttons">
<form id="debugAgainForm-{{$JobID}}">
{{$.CsrfTokenHtml}}
<a id="ai-debug-infer-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "RUNNING"}} blue {{else}} disabled {{end}}button'
data-jobid="{{$JobID}}"
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/'>
{{$.i18n.Tr "repo.online_debug"}}
</a>
</form>
</div>
{{end}}
{{if eq .JobType "HPC"}}
<div class="ui compact buttons">
<form id="debugAgainForm-{{$JobID}}">
{{$.CsrfTokenHtml}}
<a id="ai-debug-infer-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "RUNNING"}} blue {{else}} disabled {{end}}button'
data-jobid="{{$JobID}}"
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/'>
{{$.i18n.Tr "repo.start_use"}}
</a>
</form>
</div>
{{end}}
<!-- 停止任务 -->
<div class="ui compact buttons">
{{if eq .JobType "MODELSAFETY"}}
<form id="stopForm-{{$JobID}}" style="margin-left:-1px;">
{{$.CsrfTokenHtml}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop {{if eq .Status "RUNNING" "WAITING"}} blue {{else}} disabled {{end}} button'
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/modelsafety/{{$JobID}}/stop'
data-jobid="{{$JobID}}">
{{$.i18n.Tr "repo.stop"}}
</a>
</form>
{{else}}
{{if eq .JobType "DEBUG" "HPC" "BENCHMARK" "SNN4IMAGENET" "BRAINSCORE" "SNN4ECOSET" "SIM2BRAIN_SNN" "ONLINEINFERENCE"}}
<form id="stopForm-{{$JobID}}" style="margin-left:-1px;">
{{$.CsrfTokenHtml}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop {{if eq .Status "RUNNING" "WAITING"}} blue {{else}} disabled {{end}} button'
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else if eq .JobType "BENCHMARK" }}/cloudbrain/benchmark{{else if eq .ComputeResource "NPU" }}/modelarts/notebook{{end}}{{end}}/{{$JobID}}/stop'
data-jobid="{{$JobID}}" data-bootfile="{{.BootFile}}">
{{$.i18n.Tr "repo.stop"}}
</a>
</form>
{{else}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop_version {{if eq .Status "RUNNING" "WAITING"}} blue {{else}} disabled {{end}} button'
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 1}}modelarts/inference-job{{else}}cloudbrain/train-job{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}'
data-jobid="{{$JobID}}"
data-cloudbrainid="{{.Cloudbrain.ID}}"
data-version="{{.VersionName}}" data-bootfile="{{.BootFile}}">
{{$.i18n.Tr "repo.stop"}}
</a>
{{end}}
{{end}}
</div>
{{if eq .JobType "BENCHMARK"}}
<div class="ui compact buttons">
<a class="ui basic button {{if $.IsSigned}} blue{{else}} disabled{{end}}"
href="{{$.RepoLink}}/cloudbrain/{{.Cloudbrain.ID}}/rate" target="_blank">
{{$.i18n.Tr "repo.score"}}
</a>
</div>
{{end}}
<!-- 修改任务 -->
{{if and (eq .JobType "TRAIN") (not .FineTune)}}
<div class="ui compact buttons __btn_edit__">
<a style="padding: 0.5rem 1rem;" class="ui basic blue button" href='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job/{{if eq .ComputeResource "CPU/GPU"}}gpu{{else}}{{ToLower .ComputeResource}}{{end}}{{end}}/create?modify=true&id={{$JobID}}'>
{{$.i18n.Tr "repo.modelarts.modify"}}
</a>
</div>
{{end}}
<!-- 删除任务 -->
{{if eq .JobType "MODELSAFETY"}}
<form class="ui compact buttons" id="delForm-{{$JobID}}"
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/modelsafety/{{$JobID}}/del?ishomepage=true'
method="post">
{{$.CsrfTokenHtml}}
<input type="hidden" value="{{.Cloudbrain.ID}}" style="display:none" name="id" />
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}"
class="ui basic ai_delete blue button"
style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
</form>
{{else}}
<form class="ui compact buttons" id="delForm-{{$JobID}}"
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE") (eq .JobType "SNN4ECOSET") (eq .JobType "SIM2BRAIN_SNN")}}/cloudbrain{{else if eq .JobType "DEBUG" "ONLINEINFERENCE" "HPC"}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{else if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{end}}/{{$JobID}}/del?ishomepage=true'
method="post">
{{$.CsrfTokenHtml}}
<input type="hidden" value="{{.Cloudbrain.ID}}" style="display:none" name="id" />
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}"
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}/modelarts/inference-job/{{$JobID}}/del_version?ishomepage=true"
data-version="{{.VersionName}}"
data-cloudbrainid="{{.Cloudbrain.ID}}"
class="ui basic ai_delete blue button"
style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
</form>
{{end}}
</div>
</div>
</div>
{{else}}
{{$JobID := '0'}}
{{if eq .JobType "DEBUG" "TRAIN" "SNN4IMAGENET" "BRAINSCORE" "BENCHMARK" "SNN4ECOSET" "SIM2BRAIN_SNN"}}
{{$JobID = .Cloudbrain.ID}}
{{else}}
{{$JobID = .JobID}}
{{end}}
<div class="ui grid stackable item">
<div class="row">
<!-- 任务名 -->
<div class="three wide column nowrap" style="width:12% !important">
{{if eq .JobType "DEBUG"}}
<a class="title" href="" title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "INFERENCE"}}
<a class="title" href="" title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "TRAIN"}}
<a class="title" href="" title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{else if eq .JobType "BENCHMARK"}}
<a class="title" href="" title="{{.DisplayJobName}}" style="font-size: 14px;padding-right:0px">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
{{end}}
</div>
<!-- 集群 -->
<div class="one wide column text center nowrap" style="width:8% !important;">
<span
style="font-size: 12px;">{{if .Cluster}}{{.Cluster}}{{else}}--{{end}}</span>
</div>
<!-- 任务状态 -->
<div class="two wide column text center nowrap"
style="padding-left: 2.2rem !important; width: 8% !important;">
<span class="job-status" id="{{$JobID}}"
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG" "ONLINEINFERENCE" "HPC"}}{{if eq .Cloudbrain.Type 2}}/grampus/notebook{{else}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{end}}{{else if eq .JobType "INFERENCE"}}/modelarts/train-job{{else if eq .JobType "TRAIN"}}/modelarts/train-job{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}/cloudbrain{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"
data-cloudbrainid="{{.Cloudbrain.ID}}" data-datamigrate='{{$.i18n.Tr "repo.migratingData"}}' data-centerpend='{{$.i18n.Tr "repo.centerPending"}}'>
<span>
<i id="{{$JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i>
<span id="{{$JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span>
{{if eq .Status "WAITING"}}<i id="{{$JobID}}-icon-detail" class="{{.DetailedStatus}}" style="vertical-align: middle;" title='{{$.i18n.Tr (printf "repo.%s" .DetailedStatus)}}'></i>{{end}}
</span>
</span>
</div>
<!-- 任务类型 -->
{{$JobType := $.i18n.Tr (printf "cloudbrain.%s" .JobType)}}
<div class="one wide column text center nowrap" style="width:8%">
<span style="font-size: 12px;" title="{{.JobType}}">{{$JobType}}</span>
</div>
<!-- 任务创建时间 -->
<div class="two wide column text center nowrap" style="width: 8% !important;">
<span style="font-size: 12px;"
class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span>
</div>
<!-- 任务运行时间 -->
<div class="one wide column text center nowrap" style="width:6% !important;">
<span style="font-size: 12px;"
id="duration-{{$JobID}}">{{if .TrainJobDuration}}{{.TrainJobDuration}}{{else}}--{{end}}</span>
</div>
<!-- 计算资源 -->
<div class="one wide column text center nowrap" style="width:6% !important;">
<span
style="font-size: 12px;" title="{{.ComputeResource}}">{{if .ComputeResource}}{{.ComputeResource}}{{else}}--{{end}}</span>
</div>
<!-- 智算中心 -->
<div class="one wide column text center nowrap" style="width:8% !important;">
<span
style="font-size: 12px;">{{if .AiCenter}}{{.AiCenter}}{{else}}--{{end}}</span>
</div>
<!-- XPU类型 -->
<div class="one wide column text center nowrap" style="width:10% !important;">
<span style="font-size: 12px;" title="{{.CardType}}">
{{if .CardType}}{{.CardType}}{{else}}--{{end}}
</span>
</div>

<!-- 项目 -->
<div class="two wide column text center nowrap" style="width: 11%!important;">
<a href="" title="">--</a>
</div>
<div class="three wide column text center nowrap" style="width: 15%!important;">
{{if eq .JobType "DEBUG"}}
<div class="ui compact buttons">
<form id="debugAgainForm-{{$JobID}}">
{{$.CsrfTokenHtml}}
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}}
<a style="margin: 0 1rem;" id="ai-debug-{{$JobID}}"
class='ui basic disabled button'>
{{$.i18n.Tr "repo.debug"}}
</a>
{{else}}
<a id="ai-debug-{{$JobID}}" class='ui basic disabled button'>
{{$.i18n.Tr "repo.debug_again"}}
</a>
{{end}}
</form>
</div>
{{end}}
<!-- 停止任务 -->
<div class="ui compact buttons">
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class="ui basic disabled button" data-jobid="{{$JobID}}"
data-version="{{.VersionName}}">
{{$.i18n.Tr "repo.stop"}}
</a>
</div>
<!-- 删除任务 -->
<form class="ui compact buttons" id="delForm-{{$JobID}}" action='' method="post">
{{$.CsrfTokenHtml}}
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}"
class="ui basic disabled button" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
</form>
</div>
</div>
</div>

{{end}}
{{end}}
</div>
</div>
</div>
</div>
</div>
<div id="app" style="margin-top: 2rem;margin-bottom: 2rem;">
<div class="center">
<el-pagination background @current-change="handleCurrentChange" :current-page="page"
:page-sizes="[10]" :page-size="10" layout="total, sizes, prev, pager, next, jumper"
:total="{{.Page.Paginater.Total}}">
</el-pagination>
</div>
</div>
<!-- 确认模态框 -->
<div id="deletemodel">
<div class="ui basic modal">
<div class="ui icon header">
<i class="trash icon"></i> {{.i18n.Tr "cloudbrain.delete_task"}}
</div>

<div class="content">
<p>{{.i18n.Tr "cloudbrain.task_delete_confirm"}}</p>
</div>
<div class="actions">
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i> {{.i18n.Tr "cloudbrain.operate_cancel"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i> {{.i18n.Tr "cloudbrain.operate_confirm"}}
</div>
</div>
</div>
</div>

<div class="ui modal debug-again-alert">
<div class="ui message" style="background-color: rgba(242, 113, 28, 0.05);border: 1px solid rgba(242, 113, 28, 1);border-radius: 5px;">
<div style="display: flex;align-items: center;">
<i class="ri-information-line" style="font-size: 35px;color: rgba(242, 113, 28, 1);;"></i>
<div style="text-align: left;margin-left: 1rem;">
<div style="font-weight: 600;line-height: 2;">{{.i18n.Tr "repo.cloudbrain.morethanonejob1" | Safe }}</div>
<div style="color:#939393">{{.i18n.Tr "repo.cloudbrain.morethanonejob2" | Safe}}</div>
</div>
</div>
</div>
</div>
<div id="__vue-root"></div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
var editbtns = $('.__btn_edit__');
var curHref = window.location.href;
for (var i = 0, iLen = editbtns.length; i < iLen; i++) {
var buttonEl = editbtns.eq(i).find('a');
var oHref = buttonEl.attr('href');
var hasSearch = oHref.split('?').length > 1;
buttonEl.attr('href', oHref + (hasSearch ? '&' : '?') + 'backurl=' + encodeURIComponent(curHref));
}
});
</script>
<script src="{{StaticUrlPrefix}}/js/vp-cloudbrain-tasks.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 10
- 2
templates/user/dashboard/feeds.tmpl View File

@@ -1,4 +1,4 @@
<script>var Feeds={{.Feeds}};</script>
<script>var Feeds = {{.Feeds}};</script>
{{range .Feeds}}
<div class="news">
<div class="ui left">
@@ -192,13 +192,19 @@
{{$.i18n.Tr "action.task_c2ent_mlutrainjob" .GetRepoLink (Printf "%d" .Cloudbrain.ID) .RefName | Str2html}}
{{else}}
{{$.i18n.Tr "action.task_c2ent_mlutrainjob" "" "" "" | Str2html}}<span style="">{{.RefName}}{{$.i18n.Tr "repo.issues.deleted_milestone"}}</span>
{{end}}
{{end}}
{{else if eq .GetOpType 53}}
{{if .Cloudbrain}}
{{$.i18n.Tr "action.task_c2net_gpgpu_iluvatar_trainjob" .GetRepoLink (Printf "%d" .Cloudbrain.ID) .RefName | Str2html}}
{{else}}
{{$.i18n.Tr "action.task_c2net_gpgpu_iluvatar_trainjob" "" "" "" | Str2html}}<span style="">{{.RefName}}{{$.i18n.Tr "repo.issues.deleted_milestone"}}</span>
{{end}}
{{else if eq .GetOpType 54}}
{{if .Cloudbrain}}
{{$.i18n.Tr "action.task_c2netgeneraljob" .GetRepoLink (Printf "%d" .Cloudbrain.ID) .RefName | Str2html}}
{{else}}
{{$.i18n.Tr "action.task_c2netgeneraljob" "" "" "" | Str2html}}<span style="">{{.RefName}}{{$.i18n.Tr "repo.issues.deleted_milestone"}}</span>
{{end}}
{{else if eq .GetOpType 45}}
{{$.i18n.Tr "action.task_c2ent_onlineinferjob" .GetRepoLink .Content .RefName | Str2html}}
{{end}}
@@ -270,6 +276,8 @@
<span class="text grey"><i class="ri-haze-2-line icon big"></i></span>
{{else if eq .GetOpType 53}}
<span class="text grey"><i class="ri-voice-recognition-line icon big"></i></span>
{{else if eq .GetOpType 54}}
<span class="text grey"><i class="ri-voice-recognition-line icon big"></i></span>
{{else if eq .GetOpType 29}}
<span class="text grey"><i class="ri-vip-crown-line icon big"></i></span>
{{else if eq .GetOpType 30}}


+ 20
- 0
web_src/vuepages/apis/modules/cloudbrain.js View File

@@ -249,3 +249,23 @@ export const setAiTaskExportDataset = (data) => {
data: Qs.stringify(data),
});
}

// 个人中心-云脑任务列表
// job_type,job_status,ai_center,cluster,compute_source,q,page,pageSize
export const getMyAiTasks = (params) => {
return service({
url: `/api/v1/ai_task/my_list`,
method: 'get',
params: { ...params },
});
}

// 管理后台-云脑任务列表
// job_type,job_status,ai_center,cluster,compute_source,q,page,pageSize
export const getAdminAiTasks = (params) => {
return service({
url: `/api/v1/admin/ai_task/list`,
method: 'get',
params: { ...params },
});
}

+ 28
- 0
web_src/vuepages/apis/modules/common.js View File

@@ -1,4 +1,5 @@
import service from '../service';
import Qs from 'qs';

// 获取promote配置数据
export const getStaticFile = (filePathName) => {
@@ -74,3 +75,30 @@ export const getSDKCode = (params) => {
});
}

// 查询智算列表
export const getAiCenterList = () => {
return service({
url: `/resources/queue/centers`,
method: 'get',
params: {}
});
};

// 查询所有资源队列名称列表
export const getResQueueCode = (params) => {
return service({
url: `/explore/card_request/resources/queue/codes`,
method: 'get',
params,
});
};

// common form post
export const commonFormPost = (url, data) => {
return service({
url: url,
method: 'post',
params: {},
data: Qs.stringify(data),
});
}

+ 1
- 0
web_src/vuepages/apis/modules/images.js View File

@@ -27,6 +27,7 @@ export const getImages = (params) => {
python: params.python,
spec: params.spec,
trainType: params.trainType,
onlyOpenIImage: params.onlyOpenIImage,
page: params.page || 1,
pageSize: params.pageSize || 5,
}


+ 86
- 0
web_src/vuepages/components/cloudbrain/GeneralTaskCodeTips.vue View File

@@ -0,0 +1,86 @@
<template>
<div class="preview-container markdown" :class="isTaskDetail ? 'task-detail' : ''">
<div class="markdown-content">
<p class="title-h" style="font-weight:bold" v-html="$t('cloudbrainObj.generalTaskSdkCodeTip0')"></p>
<div class="content-c">
<p v-html="$t('cloudbrainObj.generalTaskSdkCodeTip1')"></p>
<pre
class="code-block"><code class="chroma language-text" v-html="$t('cloudbrainObj.generalTaskSdkCodeTip2')"></code></pre>
<p v-html="$t('cloudbrainObj.generalTaskSdkCodeTip3')"></p>
<pre class="code-block"><code class="chroma language-text">🎉 You're ready to go live at https://be22fe9f-d140-474c-81f4-30fb068157b5.tunnel.paracloud.com =&gt; http://127.0.0.1:7860
</code></pre>
<p v-html="$t('cloudbrainObj.generalTaskSdkCodeTip4')"></p>
</div>
</div>
<div class="segment-line"></div>
</div>
</template>

<script>
export default {
name: 'GeneralTaskCodeTips',
props: {
isTaskDetail: { default: false },
},
data() {
return {};
},
methods: {
},
mounted() { },
};
</script>

<style scoped lang="less">
.preview-container {
.markdown-content {
font-size: 14px;

p {
font-size: 14px;
margin-bottom: 14px;
}

.code-block {
font-size: 85%;
}
}

.segment-line {
border-top: 1px solid rgba(34, 36, 38, .15);
border-bottom: 1px solid rgba(255, 255, 255, .1);
font-size: 1rem;
line-height: 1;
height: 0;
margin-bottom: 12px !important;
}
}

.task-detail.preview-container {
.markdown-content {
.title-h {
color: #8a8e99;
font-size: 12px;
font-weight: 700;
margin-bottom: 8px;

}

.content-c {
position: relative;
border: 1px solid rgba(34, 36, 38, 0.15);
padding: 0.6em 0.8em;
overflow: auto;

p {
font-size: 13px;
margin-bottom: 10px;
}
}

.segment-line {
display: none;
}
}
}
</style>

+ 24
- 3
web_src/vuepages/components/cloudbrain/ImageSelectV1.vue View File

@@ -8,6 +8,9 @@
<el-input class="field-input" v-model="imageUrl" @input="imageChange"
:readonly="configs.computerResouce != 'GPU'" :placeholder="configs.computerResouce == 'GPU' ?
$t('cloudbrainObj.selectImagePlaceholder') : $t('cloudbrainObj.selectImage')"></el-input>
<div class="tips" v-if="errStatus && showInnerUrlTip" style="color:#e0b4b4">
{{ $t('cloudbrainObj.imageInnerUrlErrTips') }}
</div>
</div>
</div>
<div class="right-area">
@@ -152,6 +155,7 @@ export default {
version,
python,
compute_resource: this.configs.computerResouce,
spec: this.configs.computerResouce == 'GPU' && this.configs.taskType != 'GENERAL' ? -1 : this.spec,
recommend: this.dlgActiveName == 'first' ? true : undefined,
mine: this.dlgActiveName == 'second' ? true : undefined,
star: this.dlgActiveName == 'third' ? true : undefined,
@@ -194,6 +198,7 @@ export default {
dlgTotal: 0,

errStatus: false,
showInnerUrlTip: false,
};
},
watch: {
@@ -307,8 +312,10 @@ export default {
frameworkVersion: this.dlgCascaderFilter.value[1],
python: this.dlgCascaderFilter.value[2],
cuda: this.dlgCascaderFilter.value[3],
spec: this.configs.computerResouce == 'GPU' ? -1 : this.spec,
trainType: this.configs.computerResouce == 'GPU' ? undefined : getListValueWithKey(JOB_TYPE, this.configs.taskType, 'k', 'train_type'),
spec: this.configs.computerResouce == 'GPU' && this.configs.taskType != 'GENERAL' ? -1 : this.spec,
trainType: this.configs.computerResouce == 'GPU' && this.configs.taskType != 'GENERAL' ?
undefined : getListValueWithKey(JOB_TYPE, this.configs.taskType, 'k', 'train_type'),
onlyOpenIImage: this.configs.computerResouce == 'GPU' && this.configs.taskType != 'GENERAL' ? true : undefined,
}
this.dlgLoading = true;
getImages(params).then(res => {
@@ -366,8 +373,20 @@ export default {
this.dlgPage = page;
this.searchImageData();
},
checkInnerUrl(url) {
const regex = /^dockerhub\.pcl\.ac\.cn|^192\./;
if (url && regex.test(url)) {
this.showInnerUrlTip = true;
return true;
}
this.showInnerUrlTip = false;
return false;
},
check() {
if (this.required && this.imageUrl == '') {
if (this.required &&
(this.imageUrl == ''
|| (this.configs.taskType == 'GENERAL' && this.checkInnerUrl(this.imageUrl))
)) {
this.errStatus = true;
} else {
this.errStatus = false;
@@ -391,6 +410,8 @@ export default {
align-items: center;

.btn-select {
align-self: flex-start;
margin-top: 9px;
display: flex;
justify-content: center;
align-items: center;


+ 1
- 1
web_src/vuepages/components/cloudbrain/ImageSelectV2.vue View File

@@ -6,7 +6,7 @@
</div>
<div class="content" :class="errStatus ? 'error' : ''">
<el-select class="field-input" v-model="currentValue" @change="handleChange">
<el-option v-for="item in images" :key="item.image_id" :value="item.image_id"
<el-option v-for="(item, index) in images" :key="`${index}-${item.image_id}`" :value="item.image_id"
:label="item.image_name"></el-option>
</el-select>
</div>


+ 14
- 1
web_src/vuepages/components/cloudbrain/SDKCode.vue View File

@@ -1,7 +1,10 @@
<template>
<div>
<div class="title">
<GeneralTaskCodeTips v-if="showGeneralTaskCodeTip" :hasCode="codeContent"></GeneralTaskCodeTips>
<div class="title" style="font-weight:bold">
<p><span v-html="$t('cloudbrainObj.sdkCodeTip1')"></span></p>
</div>
<div class="title">
<p v-show="codeContent"><span>{{ $t('cloudbrainObj.sdkCodeTip2') }}</span></p>
</div>
<div class="content" v-show="codeContent">
@@ -25,6 +28,7 @@
import hljs from 'highlight.js';
import { getSDKCode } from '~/apis/modules/common';
import { initClipboard } from '~/utils';
import GeneralTaskCodeTips from './GeneralTaskCodeTips.vue'

export default {
name: 'SDKCode',
@@ -32,7 +36,9 @@ export default {
pageConfigs: { type: Object, required: true, },
formConfigs: { type: Object, required: true, },
data: { type: Object, required: true },
specConfigs: { type: Object, required: true },
},
components: { GeneralTaskCodeTips },
data() {
return {
repoOwnerName: location.pathname.split('/')[1],
@@ -58,6 +64,13 @@ export default {
codeHtml() {
return hljs.highlight('python', this.codeContent).value;
},
showGeneralTaskCodeTip() {
let specLen = 0;
for (let key in this.specConfigs.specs) {
specLen += this.specConfigs.specs[key].length;
}
return this.pageConfigs.taskType == 'GENERAL' && specLen;
},
},
methods: {
getCode() {


+ 12
- 4
web_src/vuepages/components/cloudbrain/details/ConfigInfo.vue View File

@@ -1,7 +1,8 @@
<template>
<div class="item-container">
<template v-for="(item, index) in configs.fields">
<div :key="item + '-' + index" v-if="item != 'dataset' && item != 'modelList' && item != 'failedReason'"
<div :key="item + '-' + index"
v-if="item != 'dataset' && item != 'modelList' && item != 'failedReason' && item != 'generalTaskCodeTips'"
class="item-block">
<div class="title"> {{ renderTitle(item) }} </div>
<div class="content" v-html="renderContent(item)"></div>
@@ -88,20 +89,26 @@
<pre><code class="python hljs" v-html="renderHljs(data.task.sdk_code)"></code></pre>
</div>
<div class="copy-btn">
<a href="javascript:;" class="ui poping up clipboard" :id="`clipboard-${sparkMD5Hash(data.task.sdk_code)}`"
data-position="top center" data-variation="inverted tiny" :data-success="$t('copySuccess')"
:data-content="$t('copy')" :data-original="$t('copy')" :data-clipboard-text="data.task.sdk_code">
<a href="javascript:;" class="ui poping up clipboard"
:id="`clipboard-${sparkMD5Hash(data.task.sdk_code)}`" data-position="top center"
data-variation="inverted tiny" :data-success="$t('copySuccess')" :data-content="$t('copy')"
:data-original="$t('copy')" :data-clipboard-text="data.task.sdk_code">
<i style="font-size:14px;" class="copy outline icon"></i>
</a>
</div>
</div>
</div>
</div>
<div :key="item + '-' + index" v-if="item == 'generalTaskCodeTips' && data.can_modify"
class="item-block item-model-code">
<GeneralTaskCodeTips :isTaskDetail="true"></GeneralTaskCodeTips>
</div>
</template>
</div>
</template>

<script>
import GeneralTaskCodeTips from '../GeneralTaskCodeTips.vue'
import { renderSpecObject, initClipboard } from '~/utils';
import { i18n } from '~/langs';
import { formatDate } from 'element-ui/lib/utils/date-util';
@@ -115,6 +122,7 @@ export default {
data: { type: Object, default: () => { return {} } },
nosdkcode: { type: Boolean, default: false },
},
components: { GeneralTaskCodeTips },
data() {
return {
clipboardHandler: null


+ 1
- 0
web_src/vuepages/components/cloudbrain/details/OperationProfile.vue View File

@@ -4,6 +4,7 @@
<p :key="index + '-1'"><b>[{{ item.reason }}]</b> <span>{{ item.timestampStr }}</span></p>
<p :key="index + '-2'">{{ item.message }}</p>
</template>
<p v-if="events.length == 0 && !loading">{{ this.$t('noMessage') }}</p>
</div>
</template>



+ 2
- 1
web_src/vuepages/const/index.js View File

@@ -12,7 +12,8 @@ export const JOB_TYPE = [
{ k: 'INFERENCE', v: i18n.t('inferenceTask'), train_type: 'TrainJob' },
{ k: 'BENCHMARK', v: i18n.t('benchmarkTask') },
{ k: 'ONLINEINFERENCE', v: i18n.t('onlineinfer') },
{ k: 'HPC', v: i18n.t('superComputeTask') }
{ k: 'HPC', v: i18n.t('superComputeTask') },
{ k: 'GENERAL', v: i18n.t('generalTask'), train_type: 'Notebook' }
];
// 资源管理
export const CLUSTERS = [{ k: 'OpenI', v: i18n.t('resourcesManagement.OpenI') }, { k: 'C2Net', v: i18n.t('resourcesManagement.C2Net') }];


+ 20
- 1
web_src/vuepages/langs/config/en-US.js View File

@@ -1,6 +1,7 @@
const en = {
loading: 'Loading...',
noData: 'No Data',
noMessage: "No Message",
noDataset: 'No Datasets',
date: 'Date',

@@ -42,6 +43,7 @@ const en = {
benchmarkTask: 'Benchmark Task',
onlineinfer: "Online Inference",
superComputeTask: "HPC Task",
generalTask: "General Task",
createPublicProject: 'Create Public Projects',
dailyPutforwardTasks: 'Daily Put Forward Tasks',
dailyPR: 'Daily PR',
@@ -190,6 +192,7 @@ const en = {
resSceneName: 'Resources Scene Name',
jobType: 'Job Type',
allJobType: 'All Job Type',
allJobStatus: 'All Job Status',
sceneType: "Community Scene Type",
allSceneType: "All Community Scene Type",
isExclusiveSpec: 'Is Exclusive Spec?',
@@ -632,7 +635,8 @@ const en = {
appName: 'App name',
appList: 'App List',
taskNameTips: 'Name must start with a lowercase letter or number,can include lowercase letter,number,_ and -,can not end with _, and can be up to 36 characters long.',
taskNameTips1: 'Name must start with a lowercase letter,can include lowercase letter,number,and -, and between 5 and 26 characters.',
taskNameTips1: 'Name must start with a lowercase letter,can include lowercase letter,number,and -, and between 5 and 26 characters.',
imageInnerUrlErrTips: 'The image url you specified is not applicable to this task. Please specify a different image.',
taskDescr: 'Description',
taskDescrPlaceholder: 'The description should not exceed 255 characters',
codeBranch: 'Code branch',
@@ -712,6 +716,8 @@ const en = {
onlineInferTaskEmptyTitle: 'Online Inference task has not been created',
onlineInferEmptyTip2: 'Dataset: Cloud Brain 1 provides CPU/GPU,Cloud Brain 2 provides Ascend NPU. And dataset also needs to be uploaded to the corresponding environment;',
superTaskEmptyTitle: 'HPC task has not been created',
generalTaskEmptyTitle: 'General task has not been created',
generalTaskEmptyTip1: 'This task type has the following characteristics and can only be used after submitting a usage application and approval on the <a href="/computingpower/demand">Computing resources</a> page:\n (1) Provide a jupyter debugging environment.\n (2) The running time is unlimited, but points are required.\n (3) Supports open ports.\n (4) Saving images is not supported, and debugging again is not supported.',
deleteConfirmTips: 'Are you sure you want to delete this task? Once this task is deleted, it cannot be recovered.',
deletingTips: 'Task deletion in progress, please wait',
tabTitDebug: 'Debug Task',
@@ -725,6 +731,7 @@ const en = {
bootFileTips: 'The startup file is the entry file that your program executes, and it must be a file ending in .py',
viewSample: 'View sample',
tabTitOnlineInference: 'Online Inference',
tabTitGeneral: 'General Task',
allResultDownload: 'All result download',
downloadDisplayMaxCountTips: 'Display up to {count} files or folders in a single directory',
file_sync_ing: "File synchronization in progress, please wait",
@@ -776,6 +783,18 @@ const en = {
codeUseDlgTitle: 'How to obtain models, datasets, and output paths in code via the c2net library',
sdkCodeTip1: 'Please use c2net library to access relevant resources in the container, which can be referred to as <a target="_blank" href="https://openi.pcl.ac.cn/docs/index.html#/cloudbrain/codepath">Help</a>.',
sdkCodeTip2: 'Based on the type of cloud brain task you choose, as well as the specified datasets, models, etc. The example code for accessing datasets and models and uploading training output are as follows:',
generalTaskSdkCodeTip0: 'How to open ports for general task',
generalTaskSdkCodeTip1: 'Run the following command to configure HTTP proxy in the environment:',
generalTaskSdkCodeTip2: 'scc tunnel http http://127.0.0.1:7860 (Port number 7860 can be customized)',
generalTaskSdkCodeTip3: 'After successful configuration, output the following information:',
generalTaskSdkCodeTip4: 'You can use the address <code>https://be22fe9f-d140-474c-81f4-30fb068157b5.tunnel.paracloud.com</code> to access the corresponding service.',

searchTaskName: 'Search Task Name...',
searchTaskNameOrCreator: 'Search Task Name/Creator...',
downloadReport: 'Download Report',
cloudbrainTaskType: 'Task Type',
repo: 'Repository',
cloudbrainTaskName: 'Cloudbrain Name',
},
superComputeObj: {
mmlSparkDescr: `The full name of MMLSpark is Microsoft Machine Learning for Apache Spark, which enables users to run customized container images and grants them root accesses within the container. Users can directly use Microsoft's MMLSpark provided by the platform.\nNote: MMLSpark is a Spark version provided by Microsoft for machine learning environments( <a target="_blank" href="https://github.com/Azure/mmlspark">https://github.com/Azure/mmlspark</a> )Regarding mmlspark, please refer to the following paper: <a target="_blank" href="https://arxiv.org/pdf/1810.08744.pdf">https://arxiv.org/pdf/1810.08744.pdf</a>`,


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

@@ -1,6 +1,7 @@
const zh = {
loading: "加载中...",
noData: "暂无数据",
noMessage: "暂无信息",
date: "日期",
noDataset: "空荡荡的,什么都没有",
confirm: "确定",
@@ -41,6 +42,7 @@ const zh = {
benchmarkTask: "评测任务",
onlineinfer: "在线推理",
superComputeTask: "超算任务",
generalTask: "通用任务",
createPublicProject: "创建公开项目",
dailyPutforwardTasks: "每日提出任务",
dailyPR: "每日提出PR",
@@ -189,6 +191,7 @@ const zh = {
resSceneName: "应用场景名称",
jobType: "任务类型",
allJobType: "全部任务类型",
allJobStatus: "全部任务状态",
sceneType: "社区资源性质",
allSceneType: "全部社区资源性质",
isExclusiveSpec: "是否专属资源规格",
@@ -648,6 +651,7 @@ const zh = {
appList: '应用列表',
taskNameTips: '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。',
taskNameTips1: '只能以小写字母开头,包含数字、小写字母和短横线(-),长度为5~26个字符。',
imageInnerUrlErrTips: '您指定的镜像地址不适用于该任务,请重新指定其它镜像。',
taskDescr: '任务描述',
taskDescrPlaceholder: '描述字数不超过255个字符',
codeBranch: '代码分支',
@@ -727,6 +731,8 @@ const zh = {
onlineInferTaskEmptyTitle: '未创建过在线推理任务',
onlineInferEmptyTip2: '数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源,在线推理使用的数据集也需要上传到对应的环境;',
superTaskEmptyTitle: '未创建过超算任务',
generalTaskEmptyTitle: '未创建过通用任务',
generalTaskEmptyTip1: '该任务类型具有如下特点,需在 <a href="/computingpower/demand">算力资源</a> 页面提交使用申请并审核通过后才能使用:\n(1)提供jupyter调试环境。\n(2)运行时间不限时,但需消耗积分。\n(3)支持开放端口。\n(4)不支持保存镜像,不支持再次调试。',
deleteConfirmTips: '您确认删除该任务么?此任务一旦删除不可恢复。',
deletingTips: '任务删除中,请稍后',
tabTitDebug: '调试任务',
@@ -740,6 +746,7 @@ const zh = {
bootFileTips: '启动文件是您程序执行的入口文件,必须是以.py结尾的文件。比如train.py、main.py、example/train.py、case/main.py。',
viewSample: '查看样例',
tabTitOnlineInference: '在线推理',
tabTitGeneral: '通用任务',
allResultDownload: '全部结果下载',
downloadDisplayMaxCountTips: '单目录下最多显示{count}个文件或文件夹',
file_sync_ing: "文件同步中,请稍侯",
@@ -791,6 +798,18 @@ const zh = {
codeUseDlgTitle: '如何在代码中通过c2net库方式获取模型、数据集和输出路径',
sdkCodeTip1: '请使用c2net库方式在容器中访问相关资源,可参考<a target="_blank" href="https://openi.pcl.ac.cn/docs/index.html#/cloudbrain/codepath">使用帮助</a>。',
sdkCodeTip2: '根据您选择的云脑任务类型,指定的数据集、模型等,访问数据集和模型,回传结果的示例代码如下:',
generalTaskSdkCodeTip0: '通用任务如何开放端口',
generalTaskSdkCodeTip1: '运行以下命令可在环境中配置HTTP代理:',
generalTaskSdkCodeTip2: 'scc tunnel http http://127.0.0.1:7860 (端口号7860可自定义)',
generalTaskSdkCodeTip3: '配置成功后,输出如下信息:',
generalTaskSdkCodeTip4: '即可使用地址<code>https://be22fe9f-d140-474c-81f4-30fb068157b5.tunnel.paracloud.com</code>访问相应服务。',

searchTaskName: '搜索任务名称...',
searchTaskNameOrCreator: '搜索任务名称/创建者...',
downloadReport: '下载此报告',
cloudbrainTaskType: '云脑任务类型',
repo: '项目',
cloudbrainTaskName: '云脑侧任务',
},
superComputeObj: {
mmlSparkDescr: `MMLSpark全称为Microsoft Machine Learning for Apache Spark,支持用户运行自制容器镜像,且赋予了用户容器内root权限。用户可直接使用平台提供的微软的MMLSpark。\n注:MMLSpark是微软提供针对机器学习环境的Spark版本(<a target="_blank" href="https://github.com/Azure/mmlspark">https://github.com/Azure/mmlspark</a>),关于mmlspark可参考论文:<a target="_blank" href="https://arxiv.org/pdf/1810.08744.pdf">https://arxiv.org/pdf/1810.08744.pdf</a>`,


+ 77
- 2
web_src/vuepages/pages/cloudbrain/configs.js View File

@@ -194,7 +194,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true },
imagev1: { required: true, type: -1 },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
@@ -589,6 +589,36 @@ export const CreatePageConfigs = {
}],
}]
}],
// 通用任务
'GENERAL': [{
url: 'grampus/general',
clusters: ['C2Net'],
'C2Net': [{
url: 'grampus/general/create?type=0',
computerResouces: ['GPU'],
'GPU': [{
url: 'grampus/general/create?type=0',
clusterType: 'C2Net',
tips2: i18n.t('cloudbrainObj.pathTips3', {
code: '/code',
dataset: '/dataset',
model: '/pretrainmodel',
}),
hideTips2: true,
form: {
taskName: { required: true, },
taskDescr: { required: false, },
taskType: {},
branchName: {},
model: { required: false, multiple: true, useExceedSize: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: {},
},
}],
}]
}],
};

export const ListPageConfigs = {
@@ -598,7 +628,8 @@ export const ListPageConfigs = {
{ key: 'TRAIN', label: i18n.t('cloudbrainObj.tabTitTrain'), url: 'modelarts/train-job?listType=all', },
{ key: 'INFERENCE', label: i18n.t('cloudbrainObj.tabTitInference'), url: 'modelarts/inference-job', },
{ key: 'BENCHMARK', label: i18n.t('cloudbrainObj.tabTitBenchmark'), url: 'cloudbrain/benchmark', },
{ key: 'ONLINEINFERENCE', label: i18n.t('cloudbrainObj.tabTitOnlineInference'), url: 'grampus/onlineinfer', }
{ key: 'ONLINEINFERENCE', label: i18n.t('cloudbrainObj.tabTitOnlineInference'), url: 'grampus/onlineinfer', },
{ key: 'GENERAL', label: i18n.t('cloudbrainObj.tabTitGeneral'), url: 'grampus/general', }
],
pages: [{
jobType: 'DEBUG',
@@ -654,6 +685,17 @@ export const ListPageConfigs = {
emptyTip0: true,
emptyTip2: i18n.t('cloudbrainObj.onlineInferEmptyTip2'),
emptyTip3: i18n.t('cloudbrainObj.debugTaskEmptyTip3', { url: 'https://openi.pcl.ac.cn/docs/index.html#/cloudbrain/infer/online-inference' }),
}, {
jobType: 'GENERAL',
sortList: getSortList([]),
jobTypeName: getListValueWithKey(JOB_TYPE, 'GENERAL'),
url: 'grampus/general',
createUrl: 'grampus/general/create?type=0',
columns: ['taskName', 'status', 'createTime', 'clusterAndComputeResource', 'creator'],
operations: ['debug', 'stop', 'modify', 'delete'],
emptyTitle: i18n.t('cloudbrainObj.generalTaskEmptyTitle'),
emptyTip1: i18n.t('cloudbrainObj.generalTaskEmptyTip1'),
emptyTip3: i18n.t('cloudbrainObj.debugTaskEmptyTip3', { url: 'https://openi.pcl.ac.cn/docs/index.html#/cloudbrain/paraCloud' }),
}]
};

@@ -1347,6 +1389,39 @@ export const DetailPageConfigs = {
}],
}]
}],
// 通用任务
'GENERAL': [{
listUrl: 'grampus/general',
clusters: ['C2Net'],
'C2Net': [{
'GPU': [{
detailUrl: 'grampus/general/',
summary: [],
operations: [],
tabs: [{
name: 'configInfo',
fields: [
'taskName', 'imagev1',
'status', 'spec',
'creator', 'aiCenter',
'branch', 'hasInternet',
'computerRes', '',
'createTime', '',
'startTime', '',
'endTime', '',
'duration', '',
'descr', '',
'failedReason',
'dataset',
'modelList',
'generalTaskCodeTips',
]
}, {
name: 'operationProfile'
}],
}],
}]
}],
};

export const getCreatePageConfigs = (url) => {


+ 15
- 9
web_src/vuepages/pages/cloudbrain/create/index.vue View File

@@ -55,11 +55,13 @@
<DatasetSelect ref="datasetRef" v-if="formCfg.dataset" v-model="state.dataset"
:required="formCfg.dataset.required"
:type="formCfg.dataset.type != undefined ? formCfg.dataset.type : -1" :repoOwnerName="repoOwnerName"
:repoName="repoName" :exceedSize="datasetSize" :useExceedSize="formCfg.dataset.useExceedSize || false">
:repoName="repoName" :exceedSize="datasetSize"
:useExceedSize="formCfg.dataset.useExceedSize || false">
</DatasetSelect>
<ModelSelect ref="modelRef" v-if="formCfg.model" v-model="state.model" :required="formCfg.model.required"
:multiple="formCfg.model.multiple" :repoOwnerName="repoOwnerName" :repoName="repoName"
:exceedSize="modelSize" :useExceedSize="formCfg.model.useExceedSize"></ModelSelect>
<ModelSelect ref="modelRef" v-if="formCfg.model" v-model="state.model"
:required="formCfg.model.required" :multiple="formCfg.model.multiple" :repoOwnerName="repoOwnerName"
:repoName="repoName" :exceedSize="modelSize" :useExceedSize="formCfg.model.useExceedSize">
</ModelSelect>
<BranchName ref="branchNameRef" v-if="formCfg.branchName" v-model="state.branchName"
:required="formCfg.branchName.required" :branches="branchList">
</BranchName>
@@ -77,15 +79,17 @@
<div class="left-area">
<div class="title"></div>
<div class="content" style="margin-left:4px;">
<el-checkbox v-model="state.isContinue">{{ this.$t('cloudbrainObj.reuseLastResult') }}</el-checkbox>
<el-checkbox v-model="state.isContinue">{{ this.$t('cloudbrainObj.reuseLastResult')
}}</el-checkbox>
<el-tooltip style="margin-left:12px" placement="top" effect="light">
<i class="question circle icon link" style="margin-top:-7px"></i>
<div slot="content">
<div style="width:200px;text-align:center;">{{ this.$t('cloudbrainObj.continue_helper') }}</div>
<div style="width:200px;text-align:center;">{{ this.$t('cloudbrainObj.continue_helper') }}
</div>
</div>
</el-tooltip>
<a :href="pageCfg.modify ? pageCfg.modify.continueSampleUrl : 'javascript:;'" target="_blank">{{
$t('modelManage.viewSamples') }}</a>
$t('modelManage.viewSamples') }}</a>
</div>
</div>
</div>
@@ -95,7 +99,8 @@
<div class="content">
<el-button type="primary"
:disabled="maskLoading || alreadyMsgBoxShow || !specConfigs.specs[state.networkType].length"
size="default" class="submit-btn" @click="submit">{{ $t('cloudbrainObj.createTask') }}</el-button>
size="default" class="submit-btn" @click="submit">{{ $t('cloudbrainObj.createTask')
}}</el-button>
<el-button class="cancel-btn" size="default" @click="cancel">{{ $t('cancel') }}</el-button>
</div>
</div>
@@ -108,7 +113,8 @@
<div class="icon ri-arrow-drop-right-line"></div>
</div>
<div class="form-right-content" v-show="showFormRight">
<SDKCode ref="sdkCodeRef" :data="state" :pageConfigs="pageCfg" :formConfigs="formCfg"></SDKCode>
<SDKCode ref="sdkCodeRef" :data="state" :pageConfigs="pageCfg" :formConfigs="formCfg"
:specConfigs="specConfigs"></SDKCode>
</div>
</div>
</div>


+ 4
- 2
web_src/vuepages/pages/cloudbrain/list/index.vue View File

@@ -431,7 +431,8 @@ export default {
opModify(row) {
if (this.operating) return;
const task = row.task;
window.location.href = `/${task.repoOwnerName}/${task.repoName}/${task.createPageUrl}?modify=true&id=${task.id}`;
const url = `/${task.repoOwnerName}/${task.repoName}/${task.createPageUrl}`;
window.location.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + `modify=true&id=${task.id}`;
},
opDelete(row) {
if (this.operating) return;
@@ -721,7 +722,8 @@ export default {
font-size: 14px;
color: #a6a6a6;
line-height: 22px;
margin-bottom: 4px;
margin-bottom: 4px;
white-space: pre-wrap;
}
}
}


+ 733
- 0
web_src/vuepages/pages/cloudbrain/tasks/index.vue View File

@@ -0,0 +1,733 @@
<template>
<div :class="isAdminPage ? 'admin-page' : ''">
<div v-if="!isAdminPage" class="searchbar-c">
<div class="ui container">
<div class="ui two column centered grid">
<div class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty"
style="margin-top:1.2rem">
<div class="ui fluid action input">
<input v-model="conds.q" :placeholder="$t('cloudbrainObj.searchTaskName')" autofocus
@keyup.enter="search" />
<button class="ui green button" @click="search">{{ $t('repos.search') }}</button>
</div>
</div>
</div>
</div>
</div>
<div v-if="isAdminPage" class="ui attached segment">
<div class="ui form ignore-dirty" style="max-width: 90%">
<div class="ui fluid action input">
<input v-model="conds.q" :placeholder="$t('cloudbrainObj.searchTaskNameOrCreator')" autofocus
@keyup.enter="search" />
<button class="ui blue button" @click="search">{{ $t('repos.search') }}</button>
</div>
</div>
</div>
<div class="content">
<div class="tips" v-if="!isAdminPage" v-html="$t('cloudbrainObj.maxTaskTips')"></div>
<div class="list-head">
<div class="filters-c">
<el-select v-model="conds.cluster" @change="changeConds">
<el-option v-for="(item) in clusterList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
<el-select v-model="conds.aicenter" @change="changeConds">
<el-option v-for="(item) in aicenterList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
<el-select v-model="conds.taskType" @change="changeConds">
<el-option v-for="(item) in taskTypeList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
<el-select v-model="conds.computeResource" @change="changeConds">
<el-option v-for="(item) in computeResourceList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
<el-select v-model="conds.status" @change="changeConds">
<el-option v-for="(item) in statusList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
</div>
<div class="right">
<a v-if="isAdminPage" class="ui compact blue basic icon button"
style="box-shadow: none !important; padding: 0.8em;" href="/admin/cloudbrains/download"><i
class="ri-download-line middle aligned icon"></i>{{ $t('cloudbrainObj.downloadReport') }}</a>
</div>
</div>
<div class="list-body table-container" v-loading="loading">
<el-table :data="tableData" style="min-width:100%" v-loading="loading" stripe>
<el-table-column :label="$t('cloudbrainObj.taskName')" align="left" header-align="left" width="240">
<template slot-scope="scope">
<a class="dispaly-job-name" :href="scope.row.task.jobLink">
{{ scope.row.task.display_job_name }}
</a>
</template>
</el-table-column>
<el-table-column :label="$t('cloudbrainObj.cluster')" align="center" header-align="center" width="125">

<template slot-scope="scope">
<span>{{ scope.row.task.clusterName }}</span>
</template>
</el-table-column>
<el-table-column prop="Status" :label="$t('status')" align="left" header-align="center" width="140">

<template slot-scope="scope">
<div class="status-wrap">
<i :class="scope.row.task.status"></i>
<span>{{ scope.row.task.status }}</span>
<i v-if="scope.row.task.detailed_status === 'dataMigrating' && scope.row.task.status === 'WAITING'"
:class="scope.row.task.detailed_status" :title="$t('cloudbrainObj.migratingData')"></i>
<i v-if="scope.row.task.detailed_status === 'centerPending' && scope.row.task.status === 'WAITING'"
:class="scope.row.task.detailed_status" :title="$t('cloudbrainObj.centerPending')"></i>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('cloudbrainObj.cloudbrainTaskType')" align="center" header-align="center"
width="125">

<template slot-scope="scope">
<span>{{ scope.row.task.jobTypeShow }}</span>
</template>
</el-table-column>
<el-table-column prop="created_unix" :label="$t('cloudbrainObj.createTime')" align="center"
header-align="center" width="190">

<template slot-scope="scope">
<span>{{ dateFormat(scope.row.task.created_unix) }}</span>
</template>
</el-table-column>
<el-table-column prop="formatted_duration" :label="$t('cloudbrainObj.runDuration')" align="center"
header-align="center" width="110">

<template slot-scope="scope">
<span>{{ scope.row.task.formatted_duration }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('cloudbrainObj.computeResource')" align="center" header-align="center"
width="160">

<template slot-scope="scope">
<span>{{ scope.row.task.computeSourceShow }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('resourcesManagement.aiCenter')" align="center" header-align="center" width="200">

<template slot-scope="scope">
<span>{{ scope.row.task.ai_center }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('resourcesManagement.accCardType')" align="center" header-align="center"
width="150">

<template slot-scope="scope">
<span>{{ scope.row.task.acc_card_type }}</span>
</template>
</el-table-column>
<el-table-column v-if="isAdminPage" :label="$t('modelManage.creator')" align="left" width="80"
header-align="center">

<template slot-scope="scope">
<div class="creator-wrap">
<a :href="'/' + scope.row.creator.name" :title="scope.row.creator.full_name">
<img :src="scope.row.creator.rel_avatar_link">
</a>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('cloudbrainObj.repo')" align="center" header-align="center" width="200">

<template slot-scope="scope">
<a class="row-repo" :href="`/${scope.row.task.repoOwnerName}/${scope.row.task.repoName}`">
{{ scope.row.task.repoOwnerName }}/{{ scope.row.task.repoName }}
</a>
</template>
</el-table-column>
<el-table-column v-if="isAdminPage" :label="$t('cloudbrainObj.cloudbrainTaskName')" align="center"
header-align="center" width="210">

<template slot-scope="scope">
<span class="ui poping up clipboard" :id="`clipboard-${scope.row.task.jobNameShow}`"
data-position="top center" data-variation="inverted tiny" :data-success="$t('copySuccess')"
:data-content="$t('copy')" :data-original="$t('copy')"
:data-clipboard-text="scope.row.task.jobNameShow">
<span :title="scope.row.task.jobNameShow" style="cursor:pointer;">
{{ scope.row.task.jobNameShow }}
</span>
</span>
</template>
</el-table-column>
<el-table-column prop="operation" :label="$t('operation')" align="left" width="280" header-align="center"
fixed="right">

<template slot-scope="scope">
<div class="op-wrap">
<a href="javascript:;" v-if="scope.row.task.operations.indexOf('onlineInfer') >= 0"
@click="opDebug(scope.row)"
:class="scope.row.can_modify && scope.row.task.canDebug ? '' : 'disabled'">{{
$t('onlineinfer')
}}</a>
<a href="javascript:;" v-if="scope.row.task.operations.indexOf('debug') >= 0 && scope.row.task.canDebug"
@click="opDebug(scope.row)"
:class="scope.row.can_modify && scope.row.task.canDebug ? '' : 'disabled'">{{
$t('cloudbrainObj.debug')
}}</a>
<a href="javascript:;"
v-if="scope.row.task.operations.indexOf('start') >= 0 && scope.row.can_modify && scope.row.task.canDebug"
@click="opDebug(scope.row)"
:class="scope.row.can_modify && scope.row.task.canDebug ? '' : 'disabled'">{{
$t('cloudbrainObj.startUse')
}}</a>
<a href="javascript:;"
v-if="scope.row.task.operations.indexOf('redebug') >= 0 && !scope.row.task.canDebug && !scope.row.task.is_file_notebook"
@click="opReDebug(scope.row)"
:class="scope.row.can_modify && scope.row.task.canReDebug ? '' : 'disabled'">{{
$t('cloudbrainObj.reDebug') }}</a>
<a href="javascript:;" v-if="scope.row.task.operations.indexOf('stop') >= 0" @click="opStop(scope.row)"
:class="scope.row.can_delete && scope.row.task.canStop ? '' : 'disabled'">{{ $t('cloudbrainObj.stop')
}}</a>
<a href="javascript:;"
v-if="scope.row.task.operations.indexOf('modify') >= 0 && scope.row.task.can_modify && !scope.row.task.is_fine_tune_task"
@click="opModify(scope.row)"
:class="scope.row.can_modify && scope.row.task.canModify ? '' : 'disabled'">{{
$t('cloudbrainObj.modify')
}}</a>
<a href="javascript:;" v-if="scope.row.task.operations.indexOf('delete') >= 0"
@click="opDelete(scope.row)"
:class="scope.row.can_delete && scope.row.task.canDelete ? '' : 'disabled'">{{
$t('cloudbrainObj.delete') }}</a>
<el-dropdown size="default" trigger="click"
v-if="scope.row.task.operations.indexOf('debugMore') >= 0 && scope.row.can_modify && scope.row.task.hasDebugMore">
<span class="el-dropdown-link">
{{ $t('cloudbrainObj.more') }}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="!scope.row.task.canSaveImage"><a target="_blank"
:href="scope.row.task.saveImageUrl">{{ $t('cloudbrainObj.commitImage') }}</a></el-dropdown-item>
<el-dropdown-item v-if="scope.row.task.canDownloadModel"><a target="_blank"
:href="scope.row.task.downloadModelUrl">{{ $t('cloudbrainObj.downloadModel')
}}</a></el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="list-foot">
<el-pagination ref="paginationRef" background @current-change="currentChange" @size-change="sizeChange"
:current-page.sync="page" :page-sizes="pageSizes" :page-size.sync="pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
</div>
<LoadingMask :loading="maskLoading" :content="maskLoadingContent"></LoadingMask>
</div>
</template>

<script>
import LoadingMask from '~/components/cloudbrain/LoadingMask.vue';
import { timeSinceUnix, getListValueWithKey } from '~/utils';
import { CloudBrainTools } from '../tools';
import { CLUSTERS, COMPUTER_RESOURCES, JOB_TYPE } from '~/const';
import { getMyAiTasks, getAdminAiTasks, getAiTaskDebugUrl, getAiTaskRestart, stopAiTask, deleteAiTask } from '~/apis/modules/cloudbrain';
import { getAiCenterList, commonFormPost } from '~/apis/modules/common';
import { getCreatePageConfigsByTask } from '../configs';

import relativeTime from 'dayjs/plugin/relativeTime';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import 'dayjs/locale/zh-cn';
import 'dayjs/locale/en';
import dayjs from 'dayjs';
import { lang } from '~/langs';

dayjs.locale(lang == 'zh-CN' ? 'zh-cn' : 'en');
dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);

const cloudBrainTools = new CloudBrainTools();

const OperationsMap = {
DEBUG: ['debug', 'redebug', 'stop', 'delete'],
TRAIN: ['stop', 'modify', 'delete'],
INFERENCE: ['stop', 'delete'],

BENCHMARK: ['stop', 'delete'],
SIM2BRAIN_SNN: ['stop', 'delete'],
SNN4ECOSET: ['stop', 'delete'],
SNN4IMAGENET: ['stop', 'delete'],
BRAINSCORE: ['stop', 'delete'],
MODELSAFETY: ['stop', 'delete'],

ONLINEINFERENCE: ['onlineInfer', 'stop', 'delete'],
HPC: ['start', 'stop', 'delete'],
GENERAL: ['debug', 'stop', 'modify', 'delete'],
};

const BenchmarkTypeList = ['BENCHMARK', 'SIM2BRAIN_SNN', 'SNN4ECOSET', 'SNN4IMAGENET', 'BRAINSCORE', 'MODELSAFETY'];
const UseJobStatusList = ['STARTING', 'RUNNING', 'RESTARTING', 'START_FAILED', 'STOPPING', 'STOPPED',
'WAITING', 'COMPLETED', 'SUCCEEDED', 'FAILED', 'KILLED', 'CREATED_FAILED'
];

export default {
data() {
return {
isAdminPage: true,
conds: {
q: '',
cluster: '',
aicenter: '',
taskType: '',
computeResource: '',
status: '',
},
clusterList: [{ k: '', v: this.$t('resourcesManagement.allCluster') }, ...CLUSTERS],
aicenterList: [{ k: '', v: this.$t('resourcesManagement.allAiCenter') }],
taskTypeList: [{ k: '', v: this.$t('resourcesManagement.allJobType') }, ...JOB_TYPE],
computeResourceList: [{ k: '', v: this.$t('resourcesManagement.allComputeResource') }, ...COMPUTER_RESOURCES],
statusList: [{ k: '', v: this.$t('resourcesManagement.allJobStatus') }, ...[...UseJobStatusList, 'other']
.map(item => ({ k: item, v: item.toLocaleUpperCase() }))],
tableData: [],
page: 1,
pageSize: 10,
total: 0,
pageSizes: [10, 20, 30, 50],
loading: false,
maskLoading: false,
maskLoadingContent: '',
};
},
components: { LoadingMask },
methods: {
getTableData() {
this.loading = true;
const params = {
job_type: this.conds.taskType,
job_status: this.conds.status,
ai_center: this.conds.aicenter,
cluster: this.conds.cluster == 'OpenI' ? 'resource_cluster_openi' : this.conds.cluster == 'C2Net' ? 'resource_cluster_c2net' : this.conds.cluster,
compute_source: this.conds.computeResource,
q: this.conds.q,
page: this.page,
pageSize: this.pageSize,
exclude_status: this.conds.status == 'other' ? UseJobStatusList.join(',') : undefined,
};
const getApi = this.isAdminPage ? getAdminAiTasks : getMyAiTasks;
getApi(params).then(res => {
this.loading = false;
res = res.data;
if (res.code == 0) {
const data = res.data;
this.canCreateTask = data.can_create_task;
this.isRepoEmpty = data.is_repo_empty;
this.total = data.total;
data.tasks.forEach(item => {
const obj = Object.assign({}, item);
delete obj.task;
const task = item.task;
Object.assign(task, obj);
task.computeSourceShow = task.compute_source == 'GPU' ? 'CPU/GPU' : task.compute_source;
task.clusterName = getListValueWithKey(CLUSTERS, task.cluster);
task.repoOwnerName = item.owner_name;
task.repoName = item.repo_name;
task.jobLink = `/${item.owner_name}/${item.repo_name}/${cloudBrainTools.getAiJobLink(task)}`;
task.createdFromNow = this.calcFromNow(task.created_unix);
task.jobNameShow = task.job_name;
task.jobTypeShow = BenchmarkTypeList.indexOf(task.job_type) >= 0 ? this.$t('benchmarkTask') : getListValueWithKey(JOB_TYPE, task.job_type);
task.operations = OperationsMap[task.job_type] || [];
const taskCreatePageCfgList = getCreatePageConfigsByTask(task);
if (taskCreatePageCfgList && taskCreatePageCfgList.length) {
task.createPageUrl = taskCreatePageCfgList[0].url;
}
cloudBrainTools.checkOperation(task);
});
this.tableData = data.tasks || [];
cloudBrainTools.initRefreshData(this.tableData);
}
}).catch(err => {
this.loading = false;
console.log(err);
});
},
search() {
this.page = 1;
this.getTableData();
},
changeConds() {
this.page = 1;
this.getTableData();
},
currentChange(page) {
this.page = page;
this.getTableData();
},
sizeChange(pageSize) {
this.page = 1;
this.pageSize = pageSize;
this.getTableData();
},
calcFromNow(unix) {
return timeSinceUnix(unix, Date.now() / 1000);
},
dateFormat(unix) {
return dayjs(unix * 1000).format('YYYY-MM-DD HH:mm:ss');
},
// operations
opDebug(row) {
if (this.operating) return;
this.operating = true;
getAiTaskDebugUrl({
repoOwnerName: row.task.repoOwnerName,
repoName: row.task.repoName,
id: row.task.id,
}).then(res => {
this.operating = false;
res = res.data;
if (res.code == 0) {
if (res.data && res.data.url) {
window.open(res.data.url)
}
} else {
this.$message({
type: 'error',
message: res.msg,
});
}
}).catch(err => {
this.operating = false;
console.log(err);
});
},
opReDebug(row) {
if (this.operating) return;
this.operating = true;
getAiTaskRestart({
repoOwnerName: row.task.repoOwnerName,
repoName: row.task.repoName,
id: row.task.id,
}).then(res => {
this.operating = false;
res = res.data;
if (res.code == 0) {
this.page = 1;
this.getTableData();
} else if (res.code == 2004) {
this.taskAlreadyDialogShow = true;
} else {
this.$message({
type: 'error',
message: res.msg,
});
}
}).catch(err => {
this.operating = false;
});
},
opStop(row) {
if (this.operating) return;
this.operating = true;
if (BenchmarkTypeList.indexOf(row.task.job_type) >= 0) {
let url = '';
if (row.task.job_type == 'MODELSAFETY') {
url = `/${row.task.repoOwnerName}/${row.task.repoName}/modelsafety/${row.task.id}/stop`;
} else if (row.task.job_type == 'BENCHMARK') {
url = `/${row.task.repoOwnerName}/${row.task.repoName}/cloudbrain/benchmark/${row.task.id}/stop`;
} else {
url = `/${row.task.repoOwnerName}/${row.task.repoName}/cloudbrain/${row.task.id}/stop`;
}
url = url + (this.isAdminPage ? '?isadminpage=true' : '');
const formData = { id: row.task.id };
commonFormPost(url, formData).then(res => {
this.operating = false;
row.task.createdFromNow = this.calcFromNow(row.task.created_unix);
cloudBrainTools.checkOperation(row.task);
this.getTableData();
}).catch(err => {
this.operating = false;
this.$message({
type: 'error',
message: this.$t('operationFailed'),
});
});
} else {
stopAiTask({
repoOwnerName: row.task.repoOwnerName,
repoName: row.task.repoName,
id: row.task.id,
}).then(res => {
this.operating = false;
res = res.data;
if (res.code == 0) {
const data = res.data;
Object.assign(row.task, data);
row.task.createdFromNow = this.calcFromNow(row.task.created_unix);
cloudBrainTools.checkOperation(row.task);
} else {
this.$message({
type: 'error',
message: res.msg
});
}
}).catch(err => {
this.operating = false;
this.$message({
type: 'error',
message: this.$t('operationFailed'),
});
});
}
},
opModify(row) {
if (this.operating) return;
const task = row.task;
const url = `/${task.repoOwnerName}/${task.repoName}/${task.createPageUrl}`;
window.location.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + `modify=true&id=${task.id}&backurl=${encodeURIComponent(window.location.href)}`;
},
opDelete(row) {
if (this.operating) return;
this.$confirm(this.$t('cloudbrainObj.deleteConfirmTips'), this.$t('tips'), {
confirmButtonText: this.$t('confirm'),
cancelButtonText: this.$t('cancel'),
type: 'warning'
}).then(() => {
this.operating = true;
this.maskLoading = true;
this.maskLoadingContent = this.$t('cloudbrainObj.deletingTips');
if (BenchmarkTypeList.indexOf(row.task.job_type) >= 0
|| (row.task.job_type == 'INFERENCE' && row.task.cluster == 'OpenI'
&& (row.task.compute_source == 'GPU' || row.task.compute_source == 'CPU/GPU'))
) {
let url = '';
if (row.task.job_type == 'MODELSAFETY') {
url = `/${row.task.repoOwnerName}/${row.task.repoName}/modelsafety/${row.task.id}/del`;
} else if (row.task.job_type == 'BENCHMARK') {
url = `/${row.task.repoOwnerName}/${row.task.repoName}/cloudbrain/benchmark/${row.task.id}/del`;
} else {
url = `/${row.task.repoOwnerName}/${row.task.repoName}/cloudbrain/${row.task.id}/del`;
}
url = url + (this.isAdminPage ? '?isadminpage=true' : '');
const formData = { id: row.task.id };
commonFormPost(url, formData).then(res => {
this.operating = false;
this.maskLoading = false;
if (this.total % this.pageSize == 1 && this.page > 1) {
this.page -= 1;
}
this.getTableData();
}).catch(err => {
this.operating = false;
this.maskLoading = false;
this.$message({
type: 'error',
message: this.$t('operationFailed'),
});
});
} else {
deleteAiTask({
repoOwnerName: row.task.repoOwnerName,
repoName: row.task.repoName,
id: row.task.id,
}).then(res => {
this.operating = false;
this.maskLoading = false;
if (this.total % this.pageSize == 1 && this.page > 1) {
this.page -= 1;
}
this.getTableData();
}).catch(err => {
this.maskLoading = false;
this.operating = false;
this.$message({
type: 'error',
message: this.$t('operationFailed'),
});
});
}
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('cancelOperate'),
});
});
},
},
beforeMount() {
this.isAdminPage = window.location.href.indexOf('admin/cloudbrains') >= 0;
getAiCenterList().then(res => {
res = res.data;
if (res.Code == 0) {
const data = res.Data || [];
data.forEach(item => {
this.aicenterList.push({
k: item.AiCenterCode,
v: item.AiCenterName,
});
});
}
}).catch(err => {
console.log(err);
});
this.getTableData();
},
mounted() { },
beforeDestroy() { },
};
</script>

<style scoped lang="less">
@import '~/components/cloudbrain/cloudbrain.less';

.searchbar-c {
padding-top: 15px;
padding-bottom: 15px;
padding-left: 0;
margin-bottom: 20px;
background-color: #f5f5f6 !important;
border-bottom: 1px solid rgba(34, 36, 38, 0.15);
}

.content {
margin: 10px 40px;

.tips {
margin-top: 1.2rem;
margin-bottom: 1.2rem;
color: #888888;
font-size: 14px;

/deep/ span {
color: #f2711c;
}
}

.list-head {
margin-top: 12px;
margin-bottom: 12px;
display: flex;
align-items: center;
justify-content: space-between;

.filters-c {
.el-select {
margin-right: 12px;
height: 36px;
width: 200px;

/deep/.el-input--small {
height: 36px;
}

/deep/.el-input__inner {
height: 36px;

&:visited {
border-color: #85b7d9;
}

&:focus {
border-color: #85b7d9;
}

&:active {
border-color: #85b7d9;
}
}
}
}
}
}

.table-container {
/deep/ .el-table__body {
td {
height: 64px;
}
}

.dispaly-job-name {
font-size: 14px;
font-weight: bold;
}

.status-wrap {
display: flex;
align-items: center;

i {
margin-right: 4px;
}
}

.creator-wrap {
display: flex;
align-items: center;
justify-content: center;

img {
height: 28px;
width: 28px;
border-radius: 100%;
}
}

.row-repo {
font-size: 14px;
}

.op-wrap {
display: flex;
align-items: center;
justify-content: center;

a {
margin: 0 10px;
font-size: 14px;
color: #1678c2;

&.disabled {
color: rgba(0, 0, 0, .87);
}
}

.el-dropdown {
margin: 0 10px;
color: rgba(0, 0, 0, .87);
cursor: pointer;
}
}
}

.list-foot {
text-align: center;
margin-top: 12px;
}


/deep/ .el-dropdown-menu__item {
a {
color: #1678c2;
}

&.is-disabled {
color: rgba(0, 0, 0, .87);
pointer-events: none;
opacity: 0.45 !important;

a {
color: rgba(0, 0, 0, .87);
pointer-events: none;
opacity: 0.45 !important;
}
}
}

.admin-page {
border: 1px solid #d4d4d5;
margin-top: 0px;
padding-top: 0;
box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15);

.content {
margin: 10px 10px;

.list-head {
margin-top: 16px;
margin-bottom: 22px;
}
}
}
</style>

+ 17
- 0
web_src/vuepages/pages/cloudbrain/tasks/vp-cloudbrain-tasks.js View File

@@ -0,0 +1,17 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import localeEn from 'element-ui/lib/locale/lang/en';
import localeZh from 'element-ui/lib/locale/lang/zh-CN';
import { i18n, lang } from '~/langs';
import App from './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,
size: 'small',
});

new Vue({
i18n,
render: (h) => h(App),
}).$mount('#__vue-root');

+ 12
- 3
web_src/vuepages/pages/cloudbrain/tools.js View File

@@ -98,7 +98,7 @@ export class CloudBrainTools {
} else {
task.canDelete = false;
}
if ((task.compute_source == 'GPU' || task.compute_source == 'GCU' || task.compute_source == 'MLU'|| task.compute_source == 'ILUVATAR-GPGPU'|| task.compute_source == 'METAX-GPGPU') && task.job_type == 'DEBUG') {
if ((task.compute_source == 'GPU' || task.compute_source == 'GCU' || task.compute_source == 'MLU' || task.compute_source == 'ILUVATAR-GPGPU' || task.compute_source == 'METAX-GPGPU') && task.job_type == 'DEBUG') {
task.hasDebugMore = true;
if (task.canDebug) {
task.canSaveImage = true;
@@ -126,12 +126,12 @@ export class CloudBrainTools {
task.canSaveImage = false;
task.canDownloadModel = false;
}
if (task.job_type == 'TRAIN') {
if (task.job_type == 'TRAIN' || task.job_type == 'GENERAL') {
task.canModify = true;
if (task.is_fine_tune_task) {
task.canModify = false;
}
if (task.can_download) {
if (task.job_type == 'TRAIN' && task.can_download) {
task.canExportOutput = true;
}
}
@@ -173,6 +173,12 @@ export class CloudBrainTools {
}
break;
case 'BENCHMARK':
case 'BRAINSCORE':
case 'SNN4IMAGENET':
case 'SNN4ECOSET':
case 'SIM2BRAIN_SNN':
case 'MODELSAFETY':
link += `cloudbrain/benchmark/${taskInfo.id}`;
break;
case 'ONLINEINFERENCE':
if (taskInfo.cluster == 'OpenI') {
@@ -188,6 +194,9 @@ export class CloudBrainTools {
case 'HPC':
link += `supercompute/job/${taskInfo.id}`;
break;
case 'GENERAL':
link += `grampus/general/${taskInfo.id}`;
break;
default:
break;
}


+ 36
- 1
web_src/vuepages/pages/computingpower/components/DemandForm.vue View File

@@ -46,6 +46,19 @@
<el-form-item label="使用组织" prop="org">
<el-input v-model="form.org" placeholder="请输入平台组织账号,多个用英文分号;分隔" maxlength="255"></el-input>
</el-form-item>
<el-form-item label="是否用于科研项目" prop="is_research_project">
<el-radio v-model="form.is_research_project" :label="false">用于非科研项目</el-radio>
<el-radio v-model="form.is_research_project" :label="true">用于科研项目</el-radio>
</el-form-item>
<el-form-item v-if="form.is_research_project" label="高校或科研机构名称" prop="institution_name">
<el-input v-model="form.institution_name" placeholder="请输入" maxlength="127"></el-input>
</el-form-item>
<el-form-item v-if="form.is_research_project" label="科研项目名称" prop="project_name">
<el-input v-model="form.project_name" placeholder="请输入" maxlength="127"></el-input>
</el-form-item>
<el-form-item v-if="form.is_research_project" label="科研项目编码" prop="project_code">
<el-input v-model="form.project_code" placeholder="请输入" maxlength="127"></el-input>
</el-form-item>
<el-form-item label="使用背景" prop="description">
<el-input type="textarea" v-model="form.description" placeholder="请输入算力使用需求,如背景介绍、具体使用场景等"
maxlength="500"></el-input>
@@ -55,7 +68,8 @@
</el-form-item>
<el-form-item v-if="type == 'edit'" label="状态" prop="description">
<div class="status-c">
<span class="status pending" v-if="data.status == 1"><i class="el-icon-stopwatch"></i><span>待处理</span></span>
<span class="status pending" v-if="data.status == 1"><i
class="el-icon-stopwatch"></i><span>待处理</span></span>
<span class="status accepted" v-if="data.status == 2"><i
class="el-icon-circle-check"></i><span>已接纳</span></span>
<span class="status refuse" v-if="data.status == 3"><i
@@ -96,6 +110,10 @@ export default {
phone_number: '',
email_address: '',
org: '',
is_research_project: false,
institution_name: '',
project_name: '',
project_code: '',
description: '',
},
formRules: {
@@ -173,6 +191,10 @@ export default {
trigger: 'blur',
}
],
is_research_project: [{ required: true, message: ' ' }],
institution_name: [{ required: true, message: ' ' }],
project_name: [{ required: true, message: ' ' }],
project_code: [{ required: true, message: ' ' }],
},
computeResourceList: [],
computeResourceCardTypeMap: {
@@ -242,6 +264,11 @@ export default {
begin_date: formatDate(this.form.use_time[0], 'yyyy-MM-dd'),
end_date: formatDate(this.form.use_time[1], 'yyyy-MM-dd'),
};
if (!this.form.is_research_project) {
submitData.institution_name = '';
submitData.project_name = '';
submitData.project_code = '';
}
delete submitData['use_time'];
if (this.type == 'add') {
this._addDemand(submitData);
@@ -351,6 +378,14 @@ export default {
min-width: 20px;
}
}

/deep/ .el-form-item__label {
line-height: 20px;
height: 40px;
display: flex;
align-items: center;
justify-content: flex-end;
}
}

.status-c {


+ 14
- 46
web_src/vuepages/pages/modelmanage/intro/index.vue View File

@@ -36,7 +36,8 @@
</div>
</div>
<div class="item-tab-c">
<div class="item-tab" :class="editTab == 'edit' ? 'focus' : ''" tab="edit" @click="changeEditTab('edit')">
<div class="item-tab" :class="editTab == 'edit' ? 'focus' : ''" tab="edit"
@click="changeEditTab('edit')">
<svg class="svg octicon-code" width="16" height="16" aria-hidden="true">
<use xlink:href="#octicon-code"></use>
</svg>
@@ -59,7 +60,8 @@
</div>
<div class="conmit-btn-c">
<el-button size="default" class="btn confirm-btn" :disabled="this.content == this.editContent"
v-loading="submitLoading" :class="this.content == this.editContent ? '_disabled_' : ''" @click="submit">
v-loading="submitLoading" :class="this.content == this.editContent ? '_disabled_' : ''"
@click="submit">
{{ $t('submit') }} </el-button>
<el-button size="default" class="btn" @click="toggleEdit(false)">{{ $t('cancel') }}</el-button>
</div>
@@ -124,7 +126,7 @@
<!-- <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>
$t('modelManage.onlineInference') }}</a>
</div>
</template>
<template v-if="onlineAddressList.length">
@@ -161,7 +163,7 @@
<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" :key="item.jobName">
<div class="detail-address" v-for="(item, index) in modelUseTaskList" :key="index">
<li class="nowrap">
<a :href="item.url" :title="item.jobName">{{ item.jobName }}</a>
</li>
@@ -181,6 +183,9 @@ import { getUrlSearchParams, setWebpackPublicPath, getListValueWithKey, transFil
import { getModelInfoByName, getMarkdownPreview, getModelIntroduce, setModelIntroduce, getModelLicenseList } from '~/apis/modules/modelmanage';
import { MODEL_ENGINES } from '~/const';
import { formatDate } from 'element-ui/lib/utils/date-util';
import { CloudBrainTools } from '~/pages/cloudbrain/tools';

const cloudBrainTools = new CloudBrainTools();

export default {
data() {
@@ -219,8 +224,6 @@ export default {
trainUsedDataList: [],
modelUseTaskList: [],
onlineUrl: '',


};
},
components: { ModelHeader, NotFound },
@@ -372,49 +375,14 @@ export default {
getFullUrlForType(data) {
const result = []
data.forEach(element => {
const detailObj = { jobName: element.displayJobName }
const detailObj = { ...element, jobName: element.displayJobName }
const prefixRepoLink = `${element.ownerName}/${element.repoName}`
if (element.type === 0) {
if (element.jobType === 'DEBUG') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/${element.id}`
} else if (element.jobType === 'TRAIN') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/train-job/${element.jobId}`
} else if (element.jobType === 'INFERENCE') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/inference-job/${element.jobId}`
} else if (element.jobType === 'BENCHMARK' || element.jobType === 'MODELSAFETY') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/benchmark/${element.id}`
} else {
}
}
if (element.type === 1) {
if (element.jobType === 'DEBUG') {
detailObj.url = `/${prefixRepoLink}/modelarts/notebook/${element.id}`
} else if (element.jobType === 'TRAIN') {
detailObj.url = `/${prefixRepoLink}/modelarts/train-job/${element.id}`
} else if (element.jobType === 'INFERENCE') {
detailObj.url = `/${prefixRepoLink}/modelarts/inference-job/${element.id}`
} else if (element.jobType === 'BENCHMARK' || element.jobType === 'MODELSAFETY') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/benchmark/${element.id}`
} else {
}

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

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

} else {
detailObj.url = `/${prefixRepoLink}/grampus/onlineinfer/${element.id}`
}
}
element.job_type = element.jobType
element.cluster = element.type == 2 ? 'C2Net' : 'OpenI'
element.compute_source = element.computeResource == 'CPU/GPU' ? 'GPU' : element.computeResource
detailObj.url = `/${prefixRepoLink}/${cloudBrainTools.getAiJobLink(element)}`
result.push(detailObj)
});

return result
}
},


+ 3
- 0
web_src/vuepages/pages/reward/point/utils.js View File

@@ -66,6 +66,9 @@ const getJobTypeLink = (record, type) => {
case 'HPC':
link += `/supercompute/job/${cloudbrain.ID}`;
break;
case 'GENERAL':
link += `/grampus/general/${cloudbrain.ID}`;
break;
default:
break;
};


Loading…
Cancel
Save