#5348 merge V20240129.patch

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


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


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


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

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


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


@@ -328,6 +354,18 @@ type GetTaskListReq struct {
Operator *models.User Operator *models.User
IsRepoOwner bool 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 { type AITaskBaseConfig struct {
ContainerSteps map[ContainerDataType]*ContainerBuildOpts `json:"container_configs"` ContainerSteps map[ContainerDataType]*ContainerBuildOpts `json:"container_configs"`
@@ -337,6 +375,9 @@ type AITaskBaseConfig struct {
DatasetsMaxNum int DatasetsMaxNum int
ModelMaxNum int ModelMaxNum int
ModelLimitSizeGB int ModelLimitSizeGB int
AutoStopDuration int64
//主动检测调式地址是否可用
DebugAddressCheck bool
} }


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


+ 37
- 0
entity/cluster.go View File

@@ -50,6 +50,43 @@ type CreateNoteBookTaskResponse struct {
Status string Status string
UserID 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 { type RestartNoteBookTaskResponse struct {
JobId string `json:"newId"` JobId string `json:"newId"`


+ 8
- 2
manager/client/grampus/grampus.go View File

@@ -440,12 +440,13 @@ func GetGrampusMetrics(jobID string, startTime int64, endTime int64, nodeId ...i
checkSetting() checkSetting()
client := getRestyClient() client := getRestyClient()
var result models.NewModelArtsMetricStatisticResult var result models.NewModelArtsMetricStatisticResult
var grampusResult models.GrampusMetricStatisticResult
url := HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/metrics" url := HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/metrics"
if len(nodeId) > 0 { if len(nodeId) > 0 {
url = HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/metrics/node/" + strconv.Itoa(nodeId[0]) url = HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/metrics/node/" + strconv.Itoa(nodeId[0])
} }
var step int64 = 60
if startTime > 0 { if startTime > 0 {
var step int64 = 60


size := int64(math.Ceil(float64(endTime-startTime)/float64(step))) + 1 size := int64(math.Ceil(float64(endTime-startTime)/float64(step))) + 1


@@ -463,13 +464,18 @@ func GetGrampusMetrics(jobID string, startTime int64, endTime int64, nodeId ...i
if err != nil { if err != nil {
return result, fmt.Errorf("resty GetTrainJobLog: %v", err) return result, fmt.Errorf("resty GetTrainJobLog: %v", err)
} }
if err = json.Unmarshal([]byte(res.String()), &result); err != nil {

if err = json.Unmarshal([]byte(res.String()), &grampusResult); err != nil {
log.Error("GetGrampusMetrics json.Unmarshal failed(%s): %v", res.String(), err.Error()) log.Error("GetGrampusMetrics json.Unmarshal failed(%s): %v", res.String(), err.Error())
return result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) return result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
} }
if res.StatusCode() != http.StatusOK { if res.StatusCode() != http.StatusOK {
return result, fmt.Errorf("Call GrampusMetrics failed(%d)", res.StatusCode()) return result, fmt.Errorf("Call GrampusMetrics failed(%d)", res.StatusCode())
} }
result = models.NewModelArtsMetricStatisticResult{
MetricsInfo: grampusResult.MetricsInfo,
Step: step,
}
return result, nil return result, nil
} }




+ 7
- 41
models/action.go View File

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


// Action represents user operation type and other information to // Action represents user operation type and other information to
@@ -114,15 +115,6 @@ type ActionShow struct {
IssueInfos []string IssueInfos []string
CommentLink string CommentLink string
Cloudbrain *CloudbrainShow4Action 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. // GetOpType gets the ActionType of this action.
@@ -153,6 +145,8 @@ func (a *Action) FilterCloudbrainInfo() {
if a.Cloudbrain.DeletedAt.IsZero() { if a.Cloudbrain.DeletedAt.IsZero() {
newCloudbrain := &Cloudbrain{} newCloudbrain := &Cloudbrain{}
newCloudbrain.ID = a.Cloudbrain.ID newCloudbrain.ID = a.Cloudbrain.ID
newCloudbrain.JobType = a.Cloudbrain.JobType
newCloudbrain.ComputeResource = a.Cloudbrain.ComputeResource
a.Cloudbrain = newCloudbrain a.Cloudbrain = newCloudbrain
} else { } else {
a.Cloudbrain = nil a.Cloudbrain = nil
@@ -304,28 +298,7 @@ func (a *Action) ToShow() *ActionShow {
if strings.Contains(a.Content, "|") && a.IsIssueAction() { if strings.Contains(a.Content, "|") && a.IsIssueAction() {
actionShow.IssueInfos = a.GetIssueInfos() 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 { if a.Repo != nil {
actionShow.RepoLink = a.GetRepoLink() actionShow.RepoLink = a.GetRepoLink()
actionShow.ShortRepoFullDisplayName = a.ShortRepoFullDisplayName() actionShow.ShortRepoFullDisplayName = a.ShortRepoFullDisplayName()
@@ -468,7 +441,8 @@ func (a *Action) IsCloudbrainAction() bool {
ActionCreateSuperComputeTask, ActionCreateSuperComputeTask,
ActionCreateGrampusILUVATARInferenceTask, ActionCreateGrampusILUVATARInferenceTask,
ActionCreateGrampusGPUInferenceTask, ActionCreateGrampusGPUInferenceTask,
ActionCreateGrampusILUVATARTrainTask:
ActionCreateGrampusILUVATARTrainTask,
ActionCreateGeneralGPUTask:
return true return true
} }
return false return false
@@ -492,14 +466,6 @@ func (a *Action) IsIssueAction() bool {
return false return false
} }


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

// GetFeedsOptions options for retrieving feeds // GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct { type GetFeedsOptions struct {
RequestedUser *User // the user we want activity for 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" const OrderByStatus = "card_request.status asc,card_request.id desc"


type CardRequest struct { 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 { 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 { func (CardRequestSpecRes) TableName() string {
@@ -88,17 +96,18 @@ type CardRequestOptions struct {
OrderBy string OrderBy string
Keyword 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 { type CardRequestShowList struct {
Total int64 `json:"total"` Total int64 `json:"total"`
@@ -106,27 +115,31 @@ type CardRequestShowList struct {
} }


type CardRequestSpecShow 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 { type RequestSpecInfo struct {
@@ -330,11 +343,17 @@ func SearchCardRequest(opts *CardRequestOptions) (int64, []*CardRequestSpecRes,
if opts.OrderBy == "" { if opts.OrderBy == "" {
opts.OrderBy = OrderByIDDesc 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"})) 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", 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.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"} "card_request.created_unix"}
var count int64 var count int64
var err error var err error
@@ -440,6 +459,6 @@ func SearchCardRequest(opts *CardRequestOptions) (int64, []*CardRequestSpecRes,
} }


func UpdateCardRequest(cardRequest *CardRequest) error { 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 return err
} }

+ 35
- 0
models/cloudbrain.go View File

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


//notebook //notebook
ModelArtsCreateQueue ModelArtsJobStatus = "CREATE_QUEUING" //免费资源创建排队中 ModelArtsCreateQueue ModelArtsJobStatus = "CREATE_QUEUING" //免费资源创建排队中
@@ -396,6 +397,19 @@ func (task *Cloudbrain) ToShow() *CloudbrainShow {
return c 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 { func (task *Cloudbrain) IsRestartTask() bool {
n, _ := x.Where("display_job_name = ?", task.DisplayJobName).Unscoped().Count(&Cloudbrain{}) n, _ := x.Where("display_job_name = ?", task.DisplayJobName).Unscoped().Count(&Cloudbrain{})
if n > 1 { 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文件任务 // 是否为在线notebook文件任务
func (task *Cloudbrain) IsFileNoteBookTask() bool { func (task *Cloudbrain) IsFileNoteBookTask() bool {
return task.JobType == string(JobTypeDebug) && task.BootFile != "" return task.JobType == string(JobTypeDebug) && task.BootFile != ""
@@ -2237,6 +2267,11 @@ type Metrics struct {


type NewModelArtsMetricStatisticResult struct { type NewModelArtsMetricStatisticResult struct {
MetricsInfo []NewModelArtsMetrics `json:"metrics"` //监控详情 MetricsInfo []NewModelArtsMetrics `json:"metrics"` //监控详情
Step int64 `json:"step"`
}

type GrampusMetricStatisticResult struct {
MetricsInfo []NewModelArtsMetrics `json:"metrics"` //监控详情
} }


type NewModelArtsMetrics struct { type NewModelArtsMetrics struct {


+ 8
- 0
models/cloudbrain_image.go View File

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


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


cond = cond.And(builder.Eq{"uid": opts.UID}) 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 { if opts.IncludeStarByMe {


@@ -661,6 +666,9 @@ func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
if opts.CloudbrainType >= 0 { if opts.CloudbrainType >= 0 {
cond = cond.And(builder.Eq{"cloudbrain_type": opts.CloudbrainType}) 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 return cond
} }


+ 19
- 0
models/resource_queue.go View File

@@ -544,6 +544,25 @@ func GetAiCenterShow(aiCenterCode, aiCenterName, language string) string {
if aiCenterName == "" { if aiCenterName == "" {
aiCenterName = aiCenterCode 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 setting.AiCenterCodeAndNameAndLocMapInfo != nil {
if info, ok := setting.AiCenterCodeAndNameAndLocMapInfo[aiCenterCode]; ok { if info, ok := setting.AiCenterCodeAndNameAndLocMapInfo[aiCenterCode]; ok {
if language == defaultLanguage { if language == defaultLanguage {


+ 2
- 1
models/task_config.go View File

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


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

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


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

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


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

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


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


type CloudbrainStartMsg struct { type CloudbrainStartMsg struct {
} }


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


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


func GetTemplateFromOperateType(operate JobOperateType) Template { func GetTemplateFromOperateType(operate JobOperateType) Template {
switch operate { switch operate {
@@ -150,6 +152,14 @@ func getCloudbrainTemplateUrl(cloudbrain models.Cloudbrain, repo *models.Reposit
} }
case string(models.JobTypeInference): case string(models.JobTypeInference):
url += "/modelarts/inference-job/" + fmt.Sprint(cloudbrain.JobID) 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 return url
} }
@@ -170,6 +180,73 @@ func getJobTypeDisplayName(jobType string) string {
string(models.JobTypeBrainScore), string(models.JobTypeBrainScore),
string(models.JobTypeSnn4Ecoset): string(models.JobTypeSnn4Ecoset):
return "推理任务" return "推理任务"
case string(models.JobTypeGeneral):
return "通用任务"
} }
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{} var FinetuneMsg = &FinetuneStartMsg{}


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


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

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


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


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

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


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


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


func SendTemplateMsg(template Template, ctx *TemplateContext, userId int64) error { 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), ClientMsgId: template.MsgId(ctx),
Data: d, 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) err, retryFlag := sendTemplateMsg(req)
if retryFlag { if retryFlag {
log.Info("SendTemplateMsg calling.userId = %d", userId) 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/auth"
"code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"time"
) )


// Notifier defines an interface to notify receiver // Notifier defines an interface to notify receiver
@@ -68,4 +69,5 @@ type Notifier interface {
NotifyCloudbrainTaskComingToFinished(cloudbrain *models.Cloudbrain, endTime timeutil.TimeStamp, account *models.PointAccount) NotifyCloudbrainTaskComingToFinished(cloudbrain *models.Cloudbrain, endTime timeutil.TimeStamp, account *models.PointAccount)
NotifyChangeFinetuneStatus(deployment *models.ModelartsDeploy) NotifyChangeFinetuneStatus(deployment *models.ModelartsDeploy)
NotifyInviteFriendRegister(inviter, invited *models.User) 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/auth"
"code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"time"
) )


// NullNotifier implements a blank notifier // NullNotifier implements a blank notifier
@@ -193,3 +194,7 @@ func (*NullNotifier) NotifyChangeFinetuneStatus(deployment *models.ModelartsDepl
func (*NullNotifier) NotifyInviteFriendRegister(inviter, invited *models.User) { 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/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"time"
) )


var ( var (
@@ -340,3 +341,10 @@ func NotifyInviteFriendRegister(inviter, invited *models.User) {
notifier.NotifyInviteFriendRegister(inviter, invited) 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/log"
"code.gitea.io/gitea/modules/notification/base" "code.gitea.io/gitea/modules/notification/base"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"time"
) )


type wechatNotifier struct { type wechatNotifier struct {
@@ -47,3 +48,9 @@ func (*wechatNotifier) NotifyChangeFinetuneStatus(deployment *models.ModelartsDe
template := wechat.FinetuneMsg template := wechat.FinetuneMsg
go wechat.SendTemplateMsg(template, &wechat.TemplateContext{ModelartsDeploy: deployment}, deployment.UserID) 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 package redis_key


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

const PREFIX = "wechat" const PREFIX = "wechat"


func WechatBindingUserIdKey(sceneStr string) string { func WechatBindingUserIdKey(sceneStr string) string {
@@ -12,3 +18,7 @@ func WechatAccessTokenKey() string {
func AccessTokenLockKey() string { func AccessTokenLockKey() string {
return KeyJoin(PREFIX, "access_token_lock") 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 TreePathOfSubscribe string


//wechat template msg config //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 //repo square config
IncubationSourceOrgName string IncubationSourceOrgName string
@@ -1717,6 +1720,9 @@ func NewContext() {
CloudbrainComingStopChargeLink = sec.Key("CLOUDBRAIN_COMING_STOP_CHARGE_LINK").MustString("请参考“个人中心 > 算力积分”页面的“积分获取说明”获取积分。") CloudbrainComingStopChargeLink = sec.Key("CLOUDBRAIN_COMING_STOP_CHARGE_LINK").MustString("请参考“个人中心 > 算力积分”页面的“积分获取说明”获取积分。")
CloudbrainComingStopRemark = sec.Key("CLOUDBRAIN_COMING_STOP_REMARK").MustString("为了不影响您使用,请您尽快获取算力积分。") CloudbrainComingStopRemark = sec.Key("CLOUDBRAIN_COMING_STOP_REMARK").MustString("为了不影响您使用,请您尽快获取算力积分。")
CloudbrainComingStopSendFlag = sec.Key("CLOUDBRAIN_COMING_STOP_SEND_FLAG").MustBool(true) 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") sec = Cfg.Section("repo-square")
IncubationSourceOrgName = sec.Key("INCUBATION_ORG_NAME").MustString("OpenI") IncubationSourceOrgName = sec.Key("INCUBATION_ORG_NAME").MustString("OpenI")


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

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


type CardReq struct { 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 { 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.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>. 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.online_infer = Online Inference
modelarts.infer_job_model = Model modelarts.infer_job_model = Model
modelarts.infer_job_model_file = Model File 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_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_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_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 binded_wechat=binded WeChat
dataset_recommended=`created dataset <a href="%s/datasets">%s</a> was set as recommended dataset` 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>` 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.morethanonejob1=您已经有 <span style="color:rgba(242, 113, 28, 1);">同类任务</span> 正在等待或运行中,请等待任务结束再创建;
cloudbrain.morethanonejob2=可以在 “<a href="/cloudbrains" target="_blank" >个人中心 > 云脑任务</a>” 查看您所有的云脑任务。 cloudbrain.morethanonejob2=可以在 “<a href="/cloudbrains" target="_blank" >个人中心 > 云脑任务</a>” 查看您所有的云脑任务。


modelarts.general_job = 通用任务
modelarts.online_infer = 在线推理 modelarts.online_infer = 在线推理
modelarts.infer_job_model = 模型名称 modelarts.infer_job_model = 模型名称
modelarts.infer_job_model_file = 模型文件 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_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_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_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=绑定微信 binded_wechat=绑定微信
dataset_recommended=`创建的数据集 <a href="%s/datasets">%s</a> 被设置为推荐数据集` dataset_recommended=`创建的数据集 <a href="%s/datasets">%s</a> 被设置为推荐数据集`
create_image=`提交了镜像 <span style="font-weight:bold;">%s</span>` 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" 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 == "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; html += recordPrefix + actionName;
const taskLink = getTaskLink(record); const taskLink = getTaskLink(record);
@@ -348,6 +348,12 @@ function getTaskLink(record){
re = re + "/supercompute/job/" + record.Cloudbrain.ID; re = re + "/supercompute/job/" + record.Cloudbrain.ID;
} else if(record.OpType == 45){ } else if(record.OpType == 45){
re = re + "/grampus/onlineinfer/" + record.Content; 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); re = encodeURI(re);
return re; return re;
@@ -519,6 +525,7 @@ var actionNameZH={
"48":"创建了ILUVATAR-GPGPU类型调试任务", "48":"创建了ILUVATAR-GPGPU类型调试任务",
"49":"创建了METAX-GPGPU类型调试任务", "49":"创建了METAX-GPGPU类型调试任务",
"53":"创建了ILUVATAR-GPGPU类型训练任务", "53":"创建了ILUVATAR-GPGPU类型训练任务",
"54":"创建了GPU类型通用任务",
}; };


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


var repoAndOrgZH={ var repoAndOrgZH={


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

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


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


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


func CloudBrains(ctx *context.Context) { func CloudBrains(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.cloudBrains")
ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminCloudBrains"] = 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) ctx.HTML(200, tplCloudBrains)

} }


func Images(ctx *context.Context) { func Images(ctx *context.Context) {


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

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


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


@@ -577,3 +582,105 @@ func GenerateSDKCode(ctx *context.Context) {
code := task.GenerateSDKCode(datasetNames, pretrainModelNames, parameterKeys, models.JobType(jobType)) code := task.GenerateSDKCode(datasetNames, pretrainModelNames, parameterKeys, models.JobType(jobType))
ctx.JSON(http.StatusOK, response.OuterSuccessWithData(map[string]string{"code": code})) 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

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


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


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


m.Group("/topics", func() { 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, IncludeStarByMe: starByMe,
UID: getUID(ctx), 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{ ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{
Data: models.GetImageAvailableColumnValues(opts), 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() useBeginTime.Unix()


opts := &models.CardRequestOptions{ 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{ opts.ListOptions = models.ListOptions{
Page: page, Page: page,
@@ -306,6 +307,10 @@ func getRequestShowList(ctx *context.Context, opts *models.CardRequestOptions, c
customShow.PhoneNumber = v.PhoneNumber customShow.PhoneNumber = v.PhoneNumber
customShow.EmailAddress = v.EmailAddress customShow.EmailAddress = v.EmailAddress
customShow.Wechat = v.Wechat 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.Contact = v.Contact
customShow.Specs = v.Specs customShow.Specs = v.Specs
customShow.Org = v.Org 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.OperationSystem = ctx.Query("os")
opts.OperationSystemVersion = ctx.Query("osVersion") opts.OperationSystemVersion = ctx.Query("osVersion")
opts.ThirdPackages = ctx.Query("thirdParty") opts.ThirdPackages = ctx.Query("thirdParty")
opts.OnlyOpenIImage = ctx.QueryBool("onlyOpenIImage")


if ctx.QueryInt64("spec") > 0 { if ctx.QueryInt64("spec") > 0 {
spec, err := models.FindSpecs(models.FindSpecsOptions{ spec, err := models.FindSpecs(models.FindSpecsOptions{
@@ -2136,107 +2137,12 @@ func SyncCloudbrainStatus() {
if task.JobType == string(models.JobTypeModelSafety) { if task.JobType == string(models.JobTypeModelSafety) {
continue 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 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)
}

+ 1
- 1
routers/repo/repo_statistic.go View File

@@ -69,7 +69,7 @@ func RepoStatisticDaily(date string) {


var error_projects = make([]string, 0) var error_projects = make([]string, 0)


pool := tunny.NewFunc(4, statisticOneRepo)
pool := tunny.NewFunc(8, statisticOneRepo)
defer pool.Close() defer pool.Close()
statisticWg.Add(len(repos)) statisticWg.Add(len(repos))
for _, repo := range repos { for _, repo := range repos {


+ 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.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("/train-job", func() {
m.Group("/:jobid", func() { m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.GrampusTrainJobShow) 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" issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "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" issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull" pull_service "code.gitea.io/gitea/services/pull"


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


func Cloudbrains(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) ctx.HTML(200, tplCloudbrains)

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




+ 86
- 11
services/ai_task_service/cluster/c2net.go View File

@@ -67,6 +67,25 @@ func (c C2NetClusterAdapter) CreateOnlineInfer(req entity.CreateNoteBookTaskRequ
return convertGrampus2NoteBookRes(jobResult), nil 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) { func (c C2NetClusterAdapter) GetNotebookImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error) {
processType := req.ComputeSource.FullName processType := req.ComputeSource.FullName
images, err := grampus.GetImages(processType, string(req.JobType)) 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 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 { func generateCommand(repoName, bootFile, datasetName string) string {


//prepare //prepare
@@ -280,6 +313,37 @@ func convertNoteBookTask2Grampus(t entity.NoteBookTask, command string) (models.
}, nil }, 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) { func getGrampusAvailableCenterIds(queues []models.ResourceQueue, imageId string, computeSource models.ComputeSource, jobType models.JobType) ([]string, error) {
if len(queues) == 0 { if len(queues) == 0 {
return []string{}, nil 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) { func (c C2NetClusterAdapter) RestartNoteBook(jobId string) (*entity.RestartNoteBookTaskResponse, error) {
res, err := grampus.RestartNotebookJob(jobId) res, err := grampus.RestartNotebookJob(jobId)
if err != nil { if err != nil {
@@ -809,16 +889,11 @@ func (c C2NetClusterAdapter) GetResourceUsage(opts entity.ClusterResourceUsageOp
nodeId := opts.NodeId nodeId := opts.NodeId
jobId := opts.JobId jobId := opts.JobId


if opts.ComputeSource == models.NPU {
startTime = 0
endTime = 0
} else {
if startTime == 0 {
startTime = time.Now().Unix() - 30*60
}
if endTime == 0 {
endTime = time.Now().Unix()
}
if startTime == 0 {
startTime = time.Now().Unix() - 30*60
}
if endTime == 0 {
endTime = time.Now().Unix()
} }
var result models.NewModelArtsMetricStatisticResult var result models.NewModelArtsMetricStatisticResult
if opts.WorkServerNumber <= 1 { if opts.WorkServerNumber <= 1 {
@@ -827,7 +902,7 @@ func (c C2NetClusterAdapter) GetResourceUsage(opts entity.ClusterResourceUsageOp
if nodeId > opts.WorkServerNumber-1 { if nodeId > opts.WorkServerNumber-1 {
return nil, response.PARAM_ERROR.ToError() return nil, response.PARAM_ERROR.ToError()
} }
result, err = grampus.GetGrampusMetrics(opts.JobId, opts.StartTime, opts.EndTime, nodeId)
result, err = grampus.GetGrampusMetrics(opts.JobId, startTime, endTime, nodeId)
} }


if err != nil { if err != nil {


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

@@ -37,6 +37,10 @@ func (c CloudbrainOneClusterAdapter) CreateOnlineInfer(req entity.CreateNoteBook
return nil, nil 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) { func (c CloudbrainOneClusterAdapter) GetNotebookImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error) {
return nil, true, nil 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 return nil, nil
} }


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

var cloudbrainTwoNotebookImages []entity.ClusterImage var cloudbrainTwoNotebookImages []entity.ClusterImage


func (c CloudbrainTwoClusterAdapter) GetNotebookImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error) { 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) GetNotebookImages(req entity.GetImageReq, centerId ...string) ([]entity.ClusterImage, bool, error)
GetTrainImages(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) 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, DatasetsMaxNum: setting.MaxDatasetNum,
ModelLimitSizeGB: setting.DEBUG_MODEL_SIZE_LIMIT_GB, ModelLimitSizeGB: setting.DEBUG_MODEL_SIZE_LIMIT_GB,
ModelMaxNum: setting.DEBUG_MODEL_NUM_LIMIT, ModelMaxNum: setting.DEBUG_MODEL_NUM_LIMIT,
DebugAddressCheck: true,
ContainerSteps: map[entity.ContainerDataType]*entity.ContainerBuildOpts{ ContainerSteps: map[entity.ContainerDataType]*entity.ContainerBuildOpts{
entity.ContainerCode: { entity.ContainerCode: {
ContainerPath: "/code", ContainerPath: "/code",
@@ -75,6 +76,7 @@ func GetCloudbrainOneNotebookConfig(opts entity.AITaskConfigKey) *entity.AITaskB
Uncompressed: true, 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 ( import (
"errors" "errors"
"net/http"
"strings"
"time" "time"


cloudbrainService "code.gitea.io/gitea/services/cloudbrain" cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
@@ -175,6 +177,25 @@ func (g DefaultAITaskTemplate) GetAllowedWorkerNum(userId int64, computeSource *
return []int{1}, nil 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) { func (g DefaultAITaskTemplate) Query(cloudbrainId int64) (*entity.AITaskDetailInfo, *response.BizError) {
//查询时先更新,然后再查询本地数据 //查询时先更新,然后再查询本地数据
g.Update(cloudbrainId) 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) log.Info("AI task is preparing.No need to update from remote.cloudbrainId=%d", cloudbrainId)
return nil 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 { 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 { } 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 { 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) return response.NewBizError(err)
} }
log.Info("updateTask success.cloudbrainId=%d", cloudbrainId) 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" "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 page := req.Page
if page <= 0 { if page <= 0 {
page = 1 page = 1
@@ -34,7 +34,7 @@ func GetAITaskList(req entity.GetTaskListReq) (*entity.AITaskListRes, *response.


for i := 0; i < len(tasks); i++ { for i := 0; i < len(tasks); i++ {
r[i] = &entity.AITaskInfo4List{ r[i] = &entity.AITaskInfo4List{
Task: entity.ConvertCloudbrainToAITaskBriefInfo(&tasks[i].Cloudbrain),
Task: entity.ConvertCloudbrainToAITaskBriefInfo(&tasks[i].Cloudbrain).ClearNonPublicFields(),
Creator: *entity.ConvertUserToBrief(&tasks[i].User), Creator: *entity.ConvertUserToBrief(&tasks[i].User),
CanModify: tasks[i].CanUserModify(req.Operator), CanModify: tasks[i].CanUserModify(req.Operator),
CanDelete: tasks[i].CanUserDelete(req.Operator, req.IsRepoOwner), CanDelete: tasks[i].CanUserDelete(req.Operator, req.IsRepoOwner),
@@ -48,3 +48,156 @@ func GetAITaskList(req entity.GetTaskListReq) (*entity.AITaskListRes, *response.
Page: page, Page: page,
}, nil }, 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/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification" "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/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/routers/response" "code.gitea.io/gitea/routers/response"
@@ -24,6 +26,7 @@ import (
"path" "path"
"strconv" "strconv"
"strings" "strings"
"time"
) )


type QueryFunc func(opts entity.JobIdAndVersionId) (*entity.QueryTaskResponse, error) 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) log.Error("QueryTaskBriefInfo GetCloudbrainByCloudbrainID err.id=%d err=%v", id, err)
return nil, err return nil, err
} }
task.LoadSpec()
return entity.ConvertCloudbrainToAITaskBriefInfo(task), nil 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 { func StopAITaskByJobNameFromRemote(task *models.Cloudbrain, queryFunc QueryListFunc, stopFunc StopFunc) error {
if task.IsTerminal() { if task.IsTerminal() {
return nil return nil
@@ -508,13 +493,16 @@ func QueryNoteBookUrl(id int64, getNoteBookUrl GetNotebookUrlFunc, fileName stri
if err != nil { if err != nil {
return "", err 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) url, err := getNoteBookUrl(cloudbrain.JobID)
if err != nil { if err != nil {
return "", err return "", err
} }
if url == "" {
return "", nil
}
if fileName != "" { if fileName != "" {
url = transferFileNotebookUrl(url, 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 { func UpdateCardRequestAdmin(cardReq api.CardReq) error {
request := models.CardRequest{ 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) beginTime, err := time.Parse(DATE_LAYOUT, cardReq.BeginDate)
if err != nil { if err != nil {
@@ -93,6 +97,10 @@ func UpdateCardRequest(cardReq api.CardReq, request *models.CardRequest) error {
request.EmailAddress = cardReq.EmailAddress request.EmailAddress = cardReq.EmailAddress
request.PhoneNumber = cardReq.PhoneNumber request.PhoneNumber = cardReq.PhoneNumber
request.Wechat = cardReq.Wechat 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) beginTime, err := time.Parse(DATE_LAYOUT, cardReq.BeginDate)
if err != nil { if err != nil {
@@ -115,21 +123,25 @@ func UpdateCardRequest(cardReq api.CardReq, request *models.CardRequest) error {
func CreateCardRequest(cardReq api.CardReq, uid int64) error { func CreateCardRequest(cardReq api.CardReq, uid int64) error {


bean := &models.CardRequest{ 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) 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) result := make([]models.GrampusImage, 0)
for _, tmp := range all { for _, tmp := range all {
computeResource := getComputeResourceByProcessType(tmp.ProcessorType) 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) result = append(result, tmp)
} }
} }


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

@@ -10,7 +10,7 @@ import (
"github.com/elliotchance/orderedmap" "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 { type ClientsManager struct {
Clients *orderedmap.OrderedMap Clients *orderedmap.OrderedMap


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

@@ -1,579 +1,10 @@
{{template "base/head" .}} {{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="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" .}} {{template "admin/navbar" .}}
<div class="ui container"> <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>
</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="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="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/onlineinfer">{{$.i18n.Tr "repo.modelarts.online_infer"}}</a>
<a class="item" href="{{.RepoLink}}/grampus/general">{{$.i18n.Tr "repo.modelarts.general_job"}}</a>
{{if MLOPS}} {{if MLOPS}}
<a class="item" href="{{MlopsHost}}/AIStudio/mlops/deploy-online/edge-inference?reponame={{.Repository.Name}}&repoId={{.Repository.ID}}" target="_blank"> <a class="item" href="{{MlopsHost}}/AIStudio/mlops/deploy-online/edge-inference?reponame={{.Repository.Name}}&repoId={{.Repository.ID}}" target="_blank">
在线推理1 在线推理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" .}} {{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="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> </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" .}} {{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}} {{range .Feeds}}
<div class="news"> <div class="news">
<div class="ui left"> <div class="ui left">
@@ -192,13 +192,19 @@
{{$.i18n.Tr "action.task_c2ent_mlutrainjob" .GetRepoLink (Printf "%d" .Cloudbrain.ID) .RefName | Str2html}} {{$.i18n.Tr "action.task_c2ent_mlutrainjob" .GetRepoLink (Printf "%d" .Cloudbrain.ID) .RefName | Str2html}}
{{else}} {{else}}
{{$.i18n.Tr "action.task_c2ent_mlutrainjob" "" "" "" | Str2html}}<span style="">{{.RefName}}{{$.i18n.Tr "repo.issues.deleted_milestone"}}</span> {{$.i18n.Tr "action.task_c2ent_mlutrainjob" "" "" "" | Str2html}}<span style="">{{.RefName}}{{$.i18n.Tr "repo.issues.deleted_milestone"}}</span>
{{end}}
{{end}}
{{else if eq .GetOpType 53}} {{else if eq .GetOpType 53}}
{{if .Cloudbrain}} {{if .Cloudbrain}}
{{$.i18n.Tr "action.task_c2net_gpgpu_iluvatar_trainjob" .GetRepoLink (Printf "%d" .Cloudbrain.ID) .RefName | Str2html}} {{$.i18n.Tr "action.task_c2net_gpgpu_iluvatar_trainjob" .GetRepoLink (Printf "%d" .Cloudbrain.ID) .RefName | Str2html}}
{{else}} {{else}}
{{$.i18n.Tr "action.task_c2net_gpgpu_iluvatar_trainjob" "" "" "" | Str2html}}<span style="">{{.RefName}}{{$.i18n.Tr "repo.issues.deleted_milestone"}}</span> {{$.i18n.Tr "action.task_c2net_gpgpu_iluvatar_trainjob" "" "" "" | Str2html}}<span style="">{{.RefName}}{{$.i18n.Tr "repo.issues.deleted_milestone"}}</span>
{{end}} {{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}} {{else if eq .GetOpType 45}}
{{$.i18n.Tr "action.task_c2ent_onlineinferjob" .GetRepoLink .Content .RefName | Str2html}} {{$.i18n.Tr "action.task_c2ent_onlineinferjob" .GetRepoLink .Content .RefName | Str2html}}
{{end}} {{end}}
@@ -270,6 +276,8 @@
<span class="text grey"><i class="ri-haze-2-line icon big"></i></span> <span class="text grey"><i class="ri-haze-2-line icon big"></i></span>
{{else if eq .GetOpType 53}} {{else if eq .GetOpType 53}}
<span class="text grey"><i class="ri-voice-recognition-line icon big"></i></span> <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}} {{else if eq .GetOpType 29}}
<span class="text grey"><i class="ri-vip-crown-line icon big"></i></span> <span class="text grey"><i class="ri-vip-crown-line icon big"></i></span>
{{else if eq .GetOpType 30}} {{else if eq .GetOpType 30}}


+ 11
- 7
web_src/less/openi.less View File

@@ -330,22 +330,26 @@ i.INFO {
.i-bg-orange { .i-bg-orange {
background-position: -495px -51px; background-position: -495px -51px;
} }
.dataMigrating{
.dataMigrating {
display: inline-block; display: inline-block;
width: 18px; width: 18px;
height: 18px; height: 18px;
margin-left: 8px;
background: url("/img/icons.svg"); background: url("/img/icons.svg");
background-position: -56px -52px;
background-position: -58px -52px;
} }
.centerPending{
.centerPending {
display: inline-block; display: inline-block;
width: 18px; width: 18px;
height: 18px; height: 18px;
margin-left: 8px;
background: url("/img/icons.svg"); background: url("/img/icons.svg");
background-position: -511px -52px;

background-position: -514px -52px;
}
.ImagePulling {
display: inline-block;
width: 18px;
height: 18px;
background: url("/img/icons.svg");
background-position: -86px -52px;
} }
.FAILED, .FAILED,
.START_FAILED, .START_FAILED,


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

@@ -249,3 +249,23 @@ export const setAiTaskExportDataset = (data) => {
data: Qs.stringify(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 service from '../service';
import Qs from 'qs';


// 获取promote配置数据 // 获取promote配置数据
export const getStaticFile = (filePathName) => { 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, python: params.python,
spec: params.spec, spec: params.spec,
trainType: params.trainType, trainType: params.trainType,
onlyOpenIImage: params.onlyOpenIImage,
page: params.page || 1, page: params.page || 1,
pageSize: params.pageSize || 5, 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" <el-input class="field-input" v-model="imageUrl" @input="imageChange"
:readonly="configs.computerResouce != 'GPU'" :placeholder="configs.computerResouce == 'GPU' ? :readonly="configs.computerResouce != 'GPU'" :placeholder="configs.computerResouce == 'GPU' ?
$t('cloudbrainObj.selectImagePlaceholder') : $t('cloudbrainObj.selectImage')"></el-input> $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> </div>
<div class="right-area"> <div class="right-area">
@@ -152,6 +155,7 @@ export default {
version, version,
python, python,
compute_resource: this.configs.computerResouce, compute_resource: this.configs.computerResouce,
spec: this.configs.computerResouce == 'GPU' && this.configs.taskType != 'GENERAL' ? -1 : this.spec,
recommend: this.dlgActiveName == 'first' ? true : undefined, recommend: this.dlgActiveName == 'first' ? true : undefined,
mine: this.dlgActiveName == 'second' ? true : undefined, mine: this.dlgActiveName == 'second' ? true : undefined,
star: this.dlgActiveName == 'third' ? true : undefined, star: this.dlgActiveName == 'third' ? true : undefined,
@@ -194,6 +198,7 @@ export default {
dlgTotal: 0, dlgTotal: 0,


errStatus: false, errStatus: false,
showInnerUrlTip: false,
}; };
}, },
watch: { watch: {
@@ -307,8 +312,10 @@ export default {
frameworkVersion: this.dlgCascaderFilter.value[1], frameworkVersion: this.dlgCascaderFilter.value[1],
python: this.dlgCascaderFilter.value[2], python: this.dlgCascaderFilter.value[2],
cuda: this.dlgCascaderFilter.value[3], 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; this.dlgLoading = true;
getImages(params).then(res => { getImages(params).then(res => {
@@ -366,8 +373,20 @@ export default {
this.dlgPage = page; this.dlgPage = page;
this.searchImageData(); 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() { check() {
if (this.required && this.imageUrl == '') {
if (this.required &&
(this.imageUrl == ''
|| (this.configs.taskType == 'GENERAL' && this.checkInnerUrl(this.imageUrl))
)) {
this.errStatus = true; this.errStatus = true;
} else { } else {
this.errStatus = false; this.errStatus = false;
@@ -391,6 +410,8 @@ export default {
align-items: center; align-items: center;


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


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

@@ -6,7 +6,7 @@
</div> </div>
<div class="content" :class="errStatus ? 'error' : ''"> <div class="content" :class="errStatus ? 'error' : ''">
<el-select class="field-input" v-model="currentValue" @change="handleChange"> <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> :label="item.image_name"></el-option>
</el-select> </el-select>
</div> </div>


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

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


export default { export default {
name: 'SDKCode', name: 'SDKCode',
@@ -32,7 +36,9 @@ export default {
pageConfigs: { type: Object, required: true, }, pageConfigs: { type: Object, required: true, },
formConfigs: { type: Object, required: true, }, formConfigs: { type: Object, required: true, },
data: { type: Object, required: true }, data: { type: Object, required: true },
specConfigs: { type: Object, required: true },
}, },
components: { GeneralTaskCodeTips },
data() { data() {
return { return {
repoOwnerName: location.pathname.split('/')[1], repoOwnerName: location.pathname.split('/')[1],
@@ -58,6 +64,13 @@ export default {
codeHtml() { codeHtml() {
return hljs.highlight('python', this.codeContent).value; 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: { methods: {
getCode() { getCode() {


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

@@ -1,7 +1,8 @@
<template> <template>
<div class="item-container"> <div class="item-container">
<template v-for="(item, index) in configs.fields"> <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"> class="item-block">
<div class="title"> {{ renderTitle(item) }} </div> <div class="title"> {{ renderTitle(item) }} </div>
<div class="content" v-html="renderContent(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> <pre><code class="python hljs" v-html="renderHljs(data.task.sdk_code)"></code></pre>
</div> </div>
<div class="copy-btn"> <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> <i style="font-size:14px;" class="copy outline icon"></i>
</a> </a>
</div> </div>
</div> </div>
</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> </template>
</div> </div>
</template> </template>


<script> <script>
import GeneralTaskCodeTips from '../GeneralTaskCodeTips.vue'
import { renderSpecObject, initClipboard } from '~/utils'; import { renderSpecObject, initClipboard } from '~/utils';
import { i18n } from '~/langs'; import { i18n } from '~/langs';
import { formatDate } from 'element-ui/lib/utils/date-util'; import { formatDate } from 'element-ui/lib/utils/date-util';
@@ -115,6 +122,7 @@ export default {
data: { type: Object, default: () => { return {} } }, data: { type: Object, default: () => { return {} } },
nosdkcode: { type: Boolean, default: false }, nosdkcode: { type: Boolean, default: false },
}, },
components: { GeneralTaskCodeTips },
data() { data() {
return { return {
clipboardHandler: null 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 + '-1'"><b>[{{ item.reason }}]</b> <span>{{ item.timestampStr }}</span></p>
<p :key="index + '-2'">{{ item.message }}</p> <p :key="index + '-2'">{{ item.message }}</p>
</template> </template>
<p v-if="events.length == 0 && !loading">{{ this.$t('noMessage') }}</p>
</div> </div>
</template> </template>




+ 3
- 2
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: 'INFERENCE', v: i18n.t('inferenceTask'), train_type: 'TrainJob' },
{ k: 'BENCHMARK', v: i18n.t('benchmarkTask') }, { k: 'BENCHMARK', v: i18n.t('benchmarkTask') },
{ k: 'ONLINEINFERENCE', v: i18n.t('onlineinfer') }, { 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') }]; export const CLUSTERS = [{ k: 'OpenI', v: i18n.t('resourcesManagement.OpenI') }, { k: 'C2Net', v: i18n.t('resourcesManagement.C2Net') }];
@@ -25,7 +26,7 @@ export const COMPUTER_RESOURCES_COLORS = {
'ILUVATAR-GPGPU': '#0038bd', 'ILUVATAR-GPGPU': '#0038bd',
'METAX-GPGPU': '#5c246a', 'METAX-GPGPU': '#5c246a',
}; };
export const ACC_CARD_TYPE = [{ k: 'T4', v: 'T4' }, { k: 'A100', v: 'A100' }, { k: 'V100', v: 'V100' }, { k: 'ASCEND910', v: 'Ascend 910' }, { k: 'ASCEND-D910B', v: 'Ascend-D910B' }, { k: 'MLU270', v: 'MLU270' }, { k: 'MLU290', v: 'MLU290' }, { k: 'RTX3080', v: 'RTX3080' }, { k: 'ENFLAME-T20', v: 'ENFLAME-T20' }, { k: 'DCU', v: 'DCU' }, { k: 'BI-V100', v: 'BI-V100' }, { k: 'MR-V100', v: 'MR-V100' }, { k: 'N100', v: 'N100' }];
export const ACC_CARD_TYPE = [{ k: 'T4', v: 'T4' }, { k: 'A100', v: 'A100' }, { k: 'V100', v: 'V100' }, { k: 'ASCEND910', v: 'Ascend 910' }, { k: 'ASCEND-D910B', v: 'Ascend-D910B' }, { k: 'MLU270', v: 'MLU270' }, { k: 'MLU290', v: 'MLU290' }, { k: 'RTX3080', v: 'RTX3080' }, { k: '3090', v: '3090' }, { k: 'ENFLAME-T20', v: 'ENFLAME-T20' }, { k: 'DCU', v: 'DCU' }, { k: 'BI-V100', v: 'BI-V100' }, { k: 'MR-V100', v: 'MR-V100' }, { k: 'N100', v: 'N100' }];
export const SPECIFICATION_STATUS = [{ k: '1', v: i18n.t('resourcesManagement.willOnShelf') }, { k: '2', v: i18n.t('resourcesManagement.onShelf') }, { k: '3', v: i18n.t('resourcesManagement.offShelf') }]; export const SPECIFICATION_STATUS = [{ k: '1', v: i18n.t('resourcesManagement.willOnShelf') }, { k: '2', v: i18n.t('resourcesManagement.onShelf') }, { k: '3', v: i18n.t('resourcesManagement.offShelf') }];
export const NETWORK_TYPE = [{ k: 1, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.noInternet')})` }, { k: 2, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.hasInternet')})` }]; export const NETWORK_TYPE = [{ k: 1, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.noInternet')})` }, { k: 2, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.hasInternet')})` }];
export const NETWORK_TYPE_VALUE = [{ k: 1, v: i18n.t('cloudbrainObj.noInternet') }, { k: 2, v: i18n.t('cloudbrainObj.hasInternet') }]; export const NETWORK_TYPE_VALUE = [{ k: 1, v: i18n.t('cloudbrainObj.noInternet') }, { k: 2, v: i18n.t('cloudbrainObj.hasInternet') }];


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

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


@@ -42,6 +43,7 @@ const en = {
benchmarkTask: 'Benchmark Task', benchmarkTask: 'Benchmark Task',
onlineinfer: "Online Inference", onlineinfer: "Online Inference",
superComputeTask: "HPC Task", superComputeTask: "HPC Task",
generalTask: "General Task",
createPublicProject: 'Create Public Projects', createPublicProject: 'Create Public Projects',
dailyPutforwardTasks: 'Daily Put Forward Tasks', dailyPutforwardTasks: 'Daily Put Forward Tasks',
dailyPR: 'Daily PR', dailyPR: 'Daily PR',
@@ -190,6 +192,7 @@ const en = {
resSceneName: 'Resources Scene Name', resSceneName: 'Resources Scene Name',
jobType: 'Job Type', jobType: 'Job Type',
allJobType: 'All Job Type', allJobType: 'All Job Type',
allJobStatus: 'All Job Status',
sceneType: "Community Scene Type", sceneType: "Community Scene Type",
allSceneType: "All Community Scene Type", allSceneType: "All Community Scene Type",
isExclusiveSpec: 'Is Exclusive Spec?', isExclusiveSpec: 'Is Exclusive Spec?',
@@ -632,7 +635,8 @@ const en = {
appName: 'App name', appName: 'App name',
appList: 'App List', 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.', 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', taskDescr: 'Description',
taskDescrPlaceholder: 'The description should not exceed 255 characters', taskDescrPlaceholder: 'The description should not exceed 255 characters',
codeBranch: 'Code branch', codeBranch: 'Code branch',
@@ -712,6 +716,8 @@ const en = {
onlineInferTaskEmptyTitle: 'Online Inference task has not been created', 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;', 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', 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.', 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', deletingTips: 'Task deletion in progress, please wait',
tabTitDebug: 'Debug Task', 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', bootFileTips: 'The startup file is the entry file that your program executes, and it must be a file ending in .py',
viewSample: 'View sample', viewSample: 'View sample',
tabTitOnlineInference: 'Online Inference', tabTitOnlineInference: 'Online Inference',
tabTitGeneral: 'General Task',
allResultDownload: 'All result download', allResultDownload: 'All result download',
downloadDisplayMaxCountTips: 'Display up to {count} files or folders in a single directory', downloadDisplayMaxCountTips: 'Display up to {count} files or folders in a single directory',
file_sync_ing: "File synchronization in progress, please wait", file_sync_ing: "File synchronization in progress, please wait",
@@ -757,6 +764,7 @@ const en = {
scrollToBottom: 'Scroll to bottom', scrollToBottom: 'Scroll to bottom',
migratingData: 'Data migration in progress', migratingData: 'Data migration in progress',
centerPending: 'Queuing in sub centers', centerPending: 'Queuing in sub centers',
imagePulling: 'Pulling image in progress',
sdkUseWay: 'c2net library usage', sdkUseWay: 'c2net library usage',


dialogTips: { dialogTips: {
@@ -776,6 +784,18 @@ const en = {
codeUseDlgTitle: 'How to obtain models, datasets, and output paths in code via the c2net library', 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>.', 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:', 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: '/root/bin/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: { 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>`, 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>`,


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

@@ -1,6 +1,7 @@
const zh = { const zh = {
loading: "加载中...", loading: "加载中...",
noData: "暂无数据", noData: "暂无数据",
noMessage: "暂无信息",
date: "日期", date: "日期",
noDataset: "空荡荡的,什么都没有", noDataset: "空荡荡的,什么都没有",
confirm: "确定", confirm: "确定",
@@ -41,6 +42,7 @@ const zh = {
benchmarkTask: "评测任务", benchmarkTask: "评测任务",
onlineinfer: "在线推理", onlineinfer: "在线推理",
superComputeTask: "超算任务", superComputeTask: "超算任务",
generalTask: "通用任务",
createPublicProject: "创建公开项目", createPublicProject: "创建公开项目",
dailyPutforwardTasks: "每日提出任务", dailyPutforwardTasks: "每日提出任务",
dailyPR: "每日提出PR", dailyPR: "每日提出PR",
@@ -189,6 +191,7 @@ const zh = {
resSceneName: "应用场景名称", resSceneName: "应用场景名称",
jobType: "任务类型", jobType: "任务类型",
allJobType: "全部任务类型", allJobType: "全部任务类型",
allJobStatus: "全部任务状态",
sceneType: "社区资源性质", sceneType: "社区资源性质",
allSceneType: "全部社区资源性质", allSceneType: "全部社区资源性质",
isExclusiveSpec: "是否专属资源规格", isExclusiveSpec: "是否专属资源规格",
@@ -648,6 +651,7 @@ const zh = {
appList: '应用列表', appList: '应用列表',
taskNameTips: '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。', taskNameTips: '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。',
taskNameTips1: '只能以小写字母开头,包含数字、小写字母和短横线(-),长度为5~26个字符。', taskNameTips1: '只能以小写字母开头,包含数字、小写字母和短横线(-),长度为5~26个字符。',
imageInnerUrlErrTips: '您指定的镜像地址不适用于该任务,请重新指定其它镜像。',
taskDescr: '任务描述', taskDescr: '任务描述',
taskDescrPlaceholder: '描述字数不超过255个字符', taskDescrPlaceholder: '描述字数不超过255个字符',
codeBranch: '代码分支', codeBranch: '代码分支',
@@ -727,6 +731,8 @@ const zh = {
onlineInferTaskEmptyTitle: '未创建过在线推理任务', onlineInferTaskEmptyTitle: '未创建过在线推理任务',
onlineInferEmptyTip2: '数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源,在线推理使用的数据集也需要上传到对应的环境;', onlineInferEmptyTip2: '数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源,在线推理使用的数据集也需要上传到对应的环境;',
superTaskEmptyTitle: '未创建过超算任务', superTaskEmptyTitle: '未创建过超算任务',
generalTaskEmptyTitle: '未创建过通用任务',
generalTaskEmptyTip1: '该任务类型具有如下特点,需在 <a href="/computingpower/demand">算力资源</a> 页面提交使用申请并审核通过后才能使用:\n(1)提供jupyter调试环境。\n(2)运行时间不限时,但需消耗积分。\n(3)支持开放端口。\n(4)不支持保存镜像,不支持再次调试。',
deleteConfirmTips: '您确认删除该任务么?此任务一旦删除不可恢复。', deleteConfirmTips: '您确认删除该任务么?此任务一旦删除不可恢复。',
deletingTips: '任务删除中,请稍后', deletingTips: '任务删除中,请稍后',
tabTitDebug: '调试任务', tabTitDebug: '调试任务',
@@ -740,6 +746,7 @@ const zh = {
bootFileTips: '启动文件是您程序执行的入口文件,必须是以.py结尾的文件。比如train.py、main.py、example/train.py、case/main.py。', bootFileTips: '启动文件是您程序执行的入口文件,必须是以.py结尾的文件。比如train.py、main.py、example/train.py、case/main.py。',
viewSample: '查看样例', viewSample: '查看样例',
tabTitOnlineInference: '在线推理', tabTitOnlineInference: '在线推理',
tabTitGeneral: '通用任务',
allResultDownload: '全部结果下载', allResultDownload: '全部结果下载',
downloadDisplayMaxCountTips: '单目录下最多显示{count}个文件或文件夹', downloadDisplayMaxCountTips: '单目录下最多显示{count}个文件或文件夹',
file_sync_ing: "文件同步中,请稍侯", file_sync_ing: "文件同步中,请稍侯",
@@ -772,6 +779,7 @@ const zh = {
scrollToBottom: '滚动到底部', scrollToBottom: '滚动到底部',
migratingData: '数据迁移中', migratingData: '数据迁移中',
centerPending: '分中心排队中', centerPending: '分中心排队中',
imagePulling: '拉取镜像中',
sdkUseWay: '通过c2net库访问方式', sdkUseWay: '通过c2net库访问方式',


dialogTips: { dialogTips: {
@@ -791,6 +799,18 @@ const zh = {
codeUseDlgTitle: '如何在代码中通过c2net库方式获取模型、数据集和输出路径', codeUseDlgTitle: '如何在代码中通过c2net库方式获取模型、数据集和输出路径',
sdkCodeTip1: '请使用c2net库方式在容器中访问相关资源,可参考<a target="_blank" href="https://openi.pcl.ac.cn/docs/index.html#/cloudbrain/codepath">使用帮助</a>。', sdkCodeTip1: '请使用c2net库方式在容器中访问相关资源,可参考<a target="_blank" href="https://openi.pcl.ac.cn/docs/index.html#/cloudbrain/codepath">使用帮助</a>。',
sdkCodeTip2: '根据您选择的云脑任务类型,指定的数据集、模型等,访问数据集和模型,回传结果的示例代码如下:', sdkCodeTip2: '根据您选择的云脑任务类型,指定的数据集、模型等,访问数据集和模型,回传结果的示例代码如下:',
generalTaskSdkCodeTip0: '通用任务如何开放端口',
generalTaskSdkCodeTip1: '运行以下命令可在环境中配置HTTP代理:',
generalTaskSdkCodeTip2: '/root/bin/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: { 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>`, 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, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true }, model: { required: false, multiple: true, useExceedSize: true },
imagev1: { required: true, type: -1 },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true }, dataset: { required: false, useExceedSize: true },
networkType: { required: true }, networkType: { required: true },
spec: { 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 = { export const ListPageConfigs = {
@@ -598,7 +628,8 @@ export const ListPageConfigs = {
{ key: 'TRAIN', label: i18n.t('cloudbrainObj.tabTitTrain'), url: 'modelarts/train-job?listType=all', }, { 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: 'INFERENCE', label: i18n.t('cloudbrainObj.tabTitInference'), url: 'modelarts/inference-job', },
{ key: 'BENCHMARK', label: i18n.t('cloudbrainObj.tabTitBenchmark'), url: 'cloudbrain/benchmark', }, { 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: [{ pages: [{
jobType: 'DEBUG', jobType: 'DEBUG',
@@ -654,6 +685,17 @@ export const ListPageConfigs = {
emptyTip0: true, emptyTip0: true,
emptyTip2: i18n.t('cloudbrainObj.onlineInferEmptyTip2'), emptyTip2: i18n.t('cloudbrainObj.onlineInferEmptyTip2'),
emptyTip3: i18n.t('cloudbrainObj.debugTaskEmptyTip3', { url: 'https://openi.pcl.ac.cn/docs/index.html#/cloudbrain/infer/online-inference' }), 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) => { 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" <DatasetSelect ref="datasetRef" v-if="formCfg.dataset" v-model="state.dataset"
:required="formCfg.dataset.required" :required="formCfg.dataset.required"
:type="formCfg.dataset.type != undefined ? formCfg.dataset.type : -1" :repoOwnerName="repoOwnerName" :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> </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" <BranchName ref="branchNameRef" v-if="formCfg.branchName" v-model="state.branchName"
:required="formCfg.branchName.required" :branches="branchList"> :required="formCfg.branchName.required" :branches="branchList">
</BranchName> </BranchName>
@@ -77,15 +79,17 @@
<div class="left-area"> <div class="left-area">
<div class="title"></div> <div class="title"></div>
<div class="content" style="margin-left:4px;"> <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"> <el-tooltip style="margin-left:12px" placement="top" effect="light">
<i class="question circle icon link" style="margin-top:-7px"></i> <i class="question circle icon link" style="margin-top:-7px"></i>
<div slot="content"> <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> </div>
</el-tooltip> </el-tooltip>
<a :href="pageCfg.modify ? pageCfg.modify.continueSampleUrl : 'javascript:;'" target="_blank">{{ <a :href="pageCfg.modify ? pageCfg.modify.continueSampleUrl : 'javascript:;'" target="_blank">{{
$t('modelManage.viewSamples') }}</a>
$t('modelManage.viewSamples') }}</a>
</div> </div>
</div> </div>
</div> </div>
@@ -95,7 +99,8 @@
<div class="content"> <div class="content">
<el-button type="primary" <el-button type="primary"
:disabled="maskLoading || alreadyMsgBoxShow || !specConfigs.specs[state.networkType].length" :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> <el-button class="cancel-btn" size="default" @click="cancel">{{ $t('cancel') }}</el-button>
</div> </div>
</div> </div>
@@ -108,7 +113,8 @@
<div class="icon ri-arrow-drop-right-line"></div> <div class="icon ri-arrow-drop-right-line"></div>
</div> </div>
<div class="form-right-content" v-show="showFormRight"> <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> </div>
</div> </div>


+ 12
- 5
web_src/vuepages/pages/cloudbrain/detail/index.vue View File

@@ -7,7 +7,7 @@
<div v-if="!loading" class="ui container"> <div v-if="!loading" class="ui container">
<div class="head-path"> <div class="head-path">
<a class="section" :href="`/${repoOwnerName}/${repoName}/debugjob?debugListType=all`">{{ <a class="section" :href="`/${repoOwnerName}/${repoName}/debugjob?debugListType=all`">{{
$t('cloudbrainObj.cloudbrain') }}</a>
$t('cloudbrainObj.cloudbrain') }}</a>
<span class="divider">/</span> <span class="divider">/</span>
<a class="section" :href="`/${repoOwnerName}/${repoName}/${pageCfg.listUrl}`">{{ pageCfg.taskTypeName }}</a> <a class="section" :href="`/${repoOwnerName}/${repoName}/${pageCfg.listUrl}`">{{ pageCfg.taskTypeName }}</a>
<span class="divider">/</span> <span class="divider">/</span>
@@ -25,8 +25,12 @@
<span>{{ $t('status') }}:</span> <span>{{ $t('status') }}:</span>
<i :class="item.task.status"></i> <i :class="item.task.status"></i>
<span>{{ item.task.status }}</span> <span>{{ item.task.status }}</span>
<i v-if="item.task.detailed_status==='dataMigrating' && item.task.status==='WAITING'" :class="item.task.detailed_status" :title="$t('cloudbrainObj.migratingData')"></i>
<i v-if="item.task.detailed_status==='centerPending' && item.task.status==='WAITING'" :class="item.task.detailed_status" :title="$t('cloudbrainObj.centerPending')"></i>
<i v-if="item.task.detailed_status === 'dataMigrating' && item.task.status === 'WAITING'"
:class="item.task.detailed_status" :title="$t('cloudbrainObj.migratingData')"></i>
<i v-if="item.task.detailed_status === 'centerPending' && item.task.status === 'WAITING'"
:class="item.task.detailed_status" :title="$t('cloudbrainObj.centerPending')"></i>
<i v-if="item.task.detailed_status === 'ImagePulling' && item.task.status === 'WAITING'"
:class="item.task.detailed_status" :title="$t('cloudbrainObj.imagePulling')"></i>
</span> </span>
<span class="task-duration"> <span class="task-duration">
<span>{{ $t('cloudbrainObj.runDuration') }}:</span><span>{{ item.task.formatted_duration }}</span> <span>{{ $t('cloudbrainObj.runDuration') }}:</span><span>{{ item.task.formatted_duration }}</span>
@@ -47,7 +51,7 @@
</ExportDataset> </ExportDataset>
</div> </div>
</div> </div>
</div>
</div>
</template> </template>
<div class="content"> <div class="content">
<el-tabs v-model="item.activeName" @tab-click="tabChange(item)"> <el-tabs v-model="item.activeName" @tab-click="tabChange(item)">
@@ -282,12 +286,14 @@ export default {
line-height: 1.5; line-height: 1.5;
cursor: default; cursor: default;
position: relative; position: relative;

.title { .title {
height: 100%; height: 100%;
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;

.title-l { .title-l {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -304,7 +310,8 @@ export default {
align-items: center; align-items: center;
margin-right: 12px; margin-right: 12px;


i {
i,
span {
margin-right: 4px; margin-right: 4px;
} }
} }


+ 26
- 18
web_src/vuepages/pages/cloudbrain/list/index.vue View File

@@ -43,8 +43,8 @@
<span>{{ scope.row.task.versionCount }}</span> <span>{{ scope.row.task.versionCount }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column v-if="pageCfg.page.columns.indexOf('modelInfo') >= 0" :label="$t('repos.model')" align="left"
header-align="left" width="100">
<el-table-column v-if="pageCfg.page.columns.indexOf('modelInfo') >= 0" :label="$t('repos.model')"
align="left" header-align="left" width="100">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ scope.row.task.model }}</span> <span>{{ scope.row.task.model }}</span>
</template> </template>
@@ -59,6 +59,8 @@
:class="scope.row.task.detailed_status" :title="$t('cloudbrainObj.migratingData')"></i> :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'" <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> :class="scope.row.task.detailed_status" :title="$t('cloudbrainObj.centerPending')"></i>
<i v-if="scope.row.task.detailed_status === 'ImagePulling' && scope.row.task.status === 'WAITING'"
:class="scope.row.task.detailed_status" :title="$t('cloudbrainObj.imagePulling')"></i>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@@ -77,7 +79,8 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column v-if="pageCfg.page.columns.indexOf('clusterAndComputeResource') >= 0" <el-table-column v-if="pageCfg.page.columns.indexOf('clusterAndComputeResource') >= 0"
:label="$t('cloudbrainObj.clusterAndComputeResource')" align="center" header-align="center" min-width="150">
:label="$t('cloudbrainObj.clusterAndComputeResource')" align="center" header-align="center"
min-width="150">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ scope.row.task.clusterName }}&nbsp;{{ scope.row.task.computeSourceShow }}</span> <span>{{ scope.row.task.clusterName }}&nbsp;{{ scope.row.task.computeSourceShow }}</span>
</template> </template>
@@ -102,36 +105,37 @@
<a href="javascript:;" v-if="pageCfg.page.operations.indexOf('onlineInfer') >= 0" <a href="javascript:;" v-if="pageCfg.page.operations.indexOf('onlineInfer') >= 0"
@click="opDebug(scope.row)" @click="opDebug(scope.row)"
:class="scope.row.can_modify && scope.row.task.canDebug ? '' : 'disabled'">{{ :class="scope.row.can_modify && scope.row.task.canDebug ? '' : 'disabled'">{{
$t('onlineinfer')
}}</a>
$t('onlineinfer')
}}</a>
<a href="javascript:;" v-if="pageCfg.page.operations.indexOf('debug') >= 0 && scope.row.task.canDebug" <a href="javascript:;" v-if="pageCfg.page.operations.indexOf('debug') >= 0 && scope.row.task.canDebug"
@click="opDebug(scope.row)" @click="opDebug(scope.row)"
:class="scope.row.can_modify && scope.row.task.canDebug ? '' : 'disabled'">{{ :class="scope.row.can_modify && scope.row.task.canDebug ? '' : 'disabled'">{{
$t('cloudbrainObj.debug')
}}</a>
$t('cloudbrainObj.debug')
}}</a>
<a href="javascript:;" <a href="javascript:;"
v-if="pageCfg.page.operations.indexOf('redebug') >= 0 && !scope.row.task.canDebug && !scope.row.task.is_file_notebook" v-if="pageCfg.page.operations.indexOf('redebug') >= 0 && !scope.row.task.canDebug && !scope.row.task.is_file_notebook"
@click="opReDebug(scope.row)" @click="opReDebug(scope.row)"
:class="scope.row.can_modify && scope.row.task.canReDebug ? '' : 'disabled'">{{ :class="scope.row.can_modify && scope.row.task.canReDebug ? '' : 'disabled'">{{
$t('cloudbrainObj.reDebug') }}</a>
$t('cloudbrainObj.reDebug') }}</a>
<a href="javascript:;" v-if="pageCfg.page.operations.indexOf('stop') >= 0" @click="opStop(scope.row)" <a href="javascript:;" v-if="pageCfg.page.operations.indexOf('stop') >= 0" @click="opStop(scope.row)"
:class="scope.row.can_delete && scope.row.task.canStop ? '' : 'disabled'">{{ $t('cloudbrainObj.stop')
}}</a>
:class="scope.row.can_delete && scope.row.task.canStop ? '' : 'disabled'">{{
$t('cloudbrainObj.stop')
}}</a>
<a href="javascript:;" <a href="javascript:;"
v-if="pageCfg.page.operations.indexOf('modify') >= 0 && scope.row.task.can_modify && !scope.row.task.is_fine_tune_task" v-if="pageCfg.page.operations.indexOf('modify') >= 0 && scope.row.task.can_modify && !scope.row.task.is_fine_tune_task"
@click="opModify(scope.row)" @click="opModify(scope.row)"
:class="scope.row.can_modify && scope.row.task.canModify ? '' : 'disabled'">{{ :class="scope.row.can_modify && scope.row.task.canModify ? '' : 'disabled'">{{
$t('cloudbrainObj.modify')
}}</a>
$t('cloudbrainObj.modify')
}}</a>
<a href="javascript:;" v-if="pageCfg.page.operations.indexOf('download') >= 0" <a href="javascript:;" v-if="pageCfg.page.operations.indexOf('download') >= 0"
@click="opDownload(scope.row)" @click="opDownload(scope.row)"
:class="scope.row.can_delete && scope.row.task.canDelete ? '' : 'disabled'">{{ :class="scope.row.can_delete && scope.row.task.canDelete ? '' : 'disabled'">{{
$t('cloudbrainObj.modelDownload')
}}</a>
$t('cloudbrainObj.modelDownload')
}}</a>
<a href="javascript:;" v-if="pageCfg.page.operations.indexOf('delete') >= 0" <a href="javascript:;" v-if="pageCfg.page.operations.indexOf('delete') >= 0"
@click="opDelete(scope.row)" @click="opDelete(scope.row)"
:class="scope.row.can_delete && scope.row.task.canDelete ? '' : 'disabled'">{{ :class="scope.row.can_delete && scope.row.task.canDelete ? '' : 'disabled'">{{
$t('cloudbrainObj.delete') }}</a>
$t('cloudbrainObj.delete') }}</a>
<el-dropdown size="default" trigger="click" <el-dropdown size="default" trigger="click"
v-if="pageCfg.page.operations.indexOf('debugMore') >= 0 && scope.row.can_modify && scope.row.task.hasDebugMore"> v-if="pageCfg.page.operations.indexOf('debugMore') >= 0 && scope.row.can_modify && scope.row.task.hasDebugMore">
<span class="el-dropdown-link"> <span class="el-dropdown-link">
@@ -139,7 +143,8 @@
</span> </span>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="!scope.row.task.canSaveImage"><a target="_blank" <el-dropdown-item :disabled="!scope.row.task.canSaveImage"><a target="_blank"
:href="scope.row.task.saveImageUrl">{{ $t('cloudbrainObj.commitImage') }}</a></el-dropdown-item>
:href="scope.row.task.saveImageUrl">{{ $t('cloudbrainObj.commitImage')
}}</a></el-dropdown-item>
<el-dropdown-item v-if="scope.row.task.canDownloadModel"><a target="_blank" <el-dropdown-item v-if="scope.row.task.canDownloadModel"><a target="_blank"
:href="scope.row.task.downloadModelUrl">{{ $t('cloudbrainObj.downloadModel') :href="scope.row.task.downloadModelUrl">{{ $t('cloudbrainObj.downloadModel')
}}</a></el-dropdown-item> }}</a></el-dropdown-item>
@@ -431,7 +436,8 @@ export default {
opModify(row) { opModify(row) {
if (this.operating) return; if (this.operating) return;
const task = row.task; 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) { opDelete(row) {
if (this.operating) return; if (this.operating) return;
@@ -621,7 +627,8 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;


i {
i,
span {
margin-right: 4px; margin-right: 4px;
} }
} }
@@ -722,6 +729,7 @@ export default {
color: #a6a6a6; color: #a6a6a6;
line-height: 22px; line-height: 22px;
margin-bottom: 4px; margin-bottom: 4px;
white-space: pre-wrap;
} }
} }
} }


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

@@ -0,0 +1,736 @@
<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>
<i v-if="scope.row.task.detailed_status === 'ImagePulling' && scope.row.task.status === 'WAITING'"
:class="scope.row.task.detailed_status" :title="$t('cloudbrainObj.imagePulling')"></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,
span {
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 { } else {
task.canDelete = false; 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; task.hasDebugMore = true;
if (task.canDebug) { if (task.canDebug) {
task.canSaveImage = true; task.canSaveImage = true;
@@ -126,12 +126,12 @@ export class CloudBrainTools {
task.canSaveImage = false; task.canSaveImage = false;
task.canDownloadModel = false; task.canDownloadModel = false;
} }
if (task.job_type == 'TRAIN') {
if (task.job_type == 'TRAIN' || task.job_type == 'GENERAL') {
task.canModify = true; task.canModify = true;
if (task.is_fine_tune_task) { if (task.is_fine_tune_task) {
task.canModify = false; task.canModify = false;
} }
if (task.can_download) {
if (task.job_type == 'TRAIN' && task.can_download) {
task.canExportOutput = true; task.canExportOutput = true;
} }
} }
@@ -173,6 +173,12 @@ export class CloudBrainTools {
} }
break; break;
case 'BENCHMARK': case 'BENCHMARK':
case 'BRAINSCORE':
case 'SNN4IMAGENET':
case 'SNN4ECOSET':
case 'SIM2BRAIN_SNN':
case 'MODELSAFETY':
link += `cloudbrain/benchmark/${taskInfo.id}`;
break; break;
case 'ONLINEINFERENCE': case 'ONLINEINFERENCE':
if (taskInfo.cluster == 'OpenI') { if (taskInfo.cluster == 'OpenI') {
@@ -188,6 +194,9 @@ export class CloudBrainTools {
case 'HPC': case 'HPC':
link += `supercompute/job/${taskInfo.id}`; link += `supercompute/job/${taskInfo.id}`;
break; break;
case 'GENERAL':
link += `grampus/general/${taskInfo.id}`;
break;
default: default:
break; break;
} }


+ 37
- 2
web_src/vuepages/pages/computingpower/components/DemandForm.vue View File

@@ -46,6 +46,19 @@
<el-form-item label="使用组织" prop="org"> <el-form-item label="使用组织" prop="org">
<el-input v-model="form.org" placeholder="请输入平台组织账号,多个用英文分号;分隔" maxlength="255"></el-input> <el-input v-model="form.org" placeholder="请输入平台组织账号,多个用英文分号;分隔" maxlength="255"></el-input>
</el-form-item> </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-form-item label="使用背景" prop="description">
<el-input type="textarea" v-model="form.description" placeholder="请输入算力使用需求,如背景介绍、具体使用场景等" <el-input type="textarea" v-model="form.description" placeholder="请输入算力使用需求,如背景介绍、具体使用场景等"
maxlength="500"></el-input> maxlength="500"></el-input>
@@ -55,7 +68,8 @@
</el-form-item> </el-form-item>
<el-form-item v-if="type == 'edit'" label="状态" prop="description"> <el-form-item v-if="type == 'edit'" label="状态" prop="description">
<div class="status-c"> <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 <span class="status accepted" v-if="data.status == 2"><i
class="el-icon-circle-check"></i><span>已接纳</span></span> class="el-icon-circle-check"></i><span>已接纳</span></span>
<span class="status refuse" v-if="data.status == 3"><i <span class="status refuse" v-if="data.status == 3"><i
@@ -96,6 +110,10 @@ export default {
phone_number: '', phone_number: '',
email_address: '', email_address: '',
org: '', org: '',
is_research_project: false,
institution_name: '',
project_name: '',
project_code: '',
description: '', description: '',
}, },
formRules: { formRules: {
@@ -173,10 +191,14 @@ export default {
trigger: 'blur', trigger: 'blur',
} }
], ],
is_research_project: [{ required: true, message: ' ' }],
institution_name: [{ required: true, message: ' ' }],
project_name: [{ required: true, message: ' ' }],
project_code: [{ required: true, message: ' ' }],
}, },
computeResourceList: [], computeResourceList: [],
computeResourceCardTypeMap: { computeResourceCardTypeMap: {
'GPU': ['A100', 'V100', 'T4'],
'GPU': ['A100', 'V100', 'T4', '3090'],
}, },
cardTypeList: [], cardTypeList: [],
resourceTypeList: [ resourceTypeList: [
@@ -242,6 +264,11 @@ export default {
begin_date: formatDate(this.form.use_time[0], 'yyyy-MM-dd'), begin_date: formatDate(this.form.use_time[0], 'yyyy-MM-dd'),
end_date: formatDate(this.form.use_time[1], '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']; delete submitData['use_time'];
if (this.type == 'add') { if (this.type == 'add') {
this._addDemand(submitData); this._addDemand(submitData);
@@ -351,6 +378,14 @@ export default {
min-width: 20px; min-width: 20px;
} }
} }

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


.status-c { .status-c {


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

@@ -36,7 +36,8 @@
</div> </div>
</div> </div>
<div class="item-tab-c"> <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"> <svg class="svg octicon-code" width="16" height="16" aria-hidden="true">
<use xlink:href="#octicon-code"></use> <use xlink:href="#octicon-code"></use>
</svg> </svg>
@@ -59,7 +60,8 @@
</div> </div>
<div class="conmit-btn-c"> <div class="conmit-btn-c">
<el-button size="default" class="btn confirm-btn" :disabled="this.content == this.editContent" <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> {{ $t('submit') }} </el-button>
<el-button size="default" class="btn" @click="toggleEdit(false)">{{ $t('cancel') }}</el-button> <el-button size="default" class="btn" @click="toggleEdit(false)">{{ $t('cancel') }}</el-button>
</div> </div>
@@ -124,7 +126,7 @@
<!-- <div class="divider-column-vertical"></div> --> <!-- <div class="divider-column-vertical"></div> -->
<div class="online-address"> <div class="online-address">
<a :href="onlineUrl" target="_blank" style="width: 50%;background: rgb(255, 255, 255);">{{ <a :href="onlineUrl" target="_blank" style="width: 50%;background: rgb(255, 255, 255);">{{
$t('modelManage.onlineInference') }}</a>
$t('modelManage.onlineInference') }}</a>
</div> </div>
</template> </template>
<template v-if="onlineAddressList.length"> <template v-if="onlineAddressList.length">
@@ -161,7 +163,7 @@
<div class="divider-column-vertical"></div> <div class="divider-column-vertical"></div>
<div class="summary" style="margin-left: 1rem;"> <div class="summary" style="margin-left: 1rem;">
<div class="title">{{ $t('modelManage.modelUseTaskList') }}:</div> <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"> <li class="nowrap">
<a :href="item.url" :title="item.jobName">{{ item.jobName }}</a> <a :href="item.url" :title="item.jobName">{{ item.jobName }}</a>
</li> </li>
@@ -181,6 +183,9 @@ import { getUrlSearchParams, setWebpackPublicPath, getListValueWithKey, transFil
import { getModelInfoByName, getMarkdownPreview, getModelIntroduce, setModelIntroduce, getModelLicenseList } from '~/apis/modules/modelmanage'; import { getModelInfoByName, getMarkdownPreview, getModelIntroduce, setModelIntroduce, getModelLicenseList } from '~/apis/modules/modelmanage';
import { MODEL_ENGINES } from '~/const'; import { MODEL_ENGINES } from '~/const';
import { formatDate } from 'element-ui/lib/utils/date-util'; import { formatDate } from 'element-ui/lib/utils/date-util';
import { CloudBrainTools } from '~/pages/cloudbrain/tools';

const cloudBrainTools = new CloudBrainTools();


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


}; };
}, },
components: { ModelHeader, NotFound }, components: { ModelHeader, NotFound },
@@ -372,49 +375,14 @@ export default {
getFullUrlForType(data) { getFullUrlForType(data) {
const result = [] const result = []
data.forEach(element => { data.forEach(element => {
const detailObj = { jobName: element.displayJobName }
const detailObj = { ...element, jobName: element.displayJobName }
const prefixRepoLink = `${element.ownerName}/${element.repoName}` 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) result.push(detailObj)
}); });

return result return result
} }
}, },


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

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


Loading…
Cancel
Save