diff --git a/agent/app/api/v2/entry.go b/agent/app/api/v2/entry.go index 54f34ae2a..23ac135dc 100644 --- a/agent/app/api/v2/entry.go +++ b/agent/app/api/v2/entry.go @@ -18,10 +18,9 @@ var ( appInstallService = service.NewIAppInstalledService() appIgnoreUpgradeService = service.NewIAppIgnoreUpgradeService() - modelDownloaderService = service.NewIModelDownloaderService() - aiToolService = service.NewIAIToolService() - mcpServerService = service.NewIMcpServerService() - agentService = service.NewIAgentService() + aiToolService = service.NewIAIToolService() + mcpServerService = service.NewIMcpServerService() + agentService = service.NewIAgentService() containerService = service.NewIContainerService() composeTemplateService = service.NewIComposeTemplateService() diff --git a/agent/app/api/v2/model_downloader.go b/agent/app/api/v2/model_downloader.go deleted file mode 100644 index a6381e074..000000000 --- a/agent/app/api/v2/model_downloader.go +++ /dev/null @@ -1,399 +0,0 @@ -package v2 - -import ( - "github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" - "github.com/1Panel-dev/1Panel/agent/app/dto" - "github.com/1Panel-dev/1Panel/agent/app/service" - "github.com/gin-gonic/gin" -) - -func handleModelDownloaderError(c *gin.Context, err error) { - if service.IsModelDownloaderInputError(err) { - helper.BadRequest(c, err) - return - } - helper.InternalServer(c, err) -} - -// @Tags AI -// @Summary Search Hugging Face models -// @Accept json -// @Param request body dto.ModelSearch true "request" -// @Produce json -// @Success 200 {object} dto.PageResult{items=[]dto.ModelItem} -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/hf/search [post] -func (b *BaseApi) SearchHFModels(c *gin.Context) { - var req dto.ModelSearch - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - total, items, err := modelDownloaderService.SearchHFModels(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, dto.PageResult{Total: total, Items: items}) -} - -// @Tags AI -// @Summary Get Hugging Face model details -// @Accept json -// @Param request body dto.ModelInfoReq true "request" -// @Produce json -// @Success 200 {object} dto.ModelInfo -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/hf/info [post] -func (b *BaseApi) GetHFModelInfo(c *gin.Context) { - var req dto.ModelInfoReq - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - item, err := modelDownloaderService.GetHFModelInfo(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, item) -} - -// @Tags AI -// @Summary Download Hugging Face model -// @Description Creates an asynchronous download task. Query /ai/model/downloader/tasks/search for progress. -// @Accept json -// @Param request body dto.ModelDownload true "request" -// @Produce json -// @Success 200 {object} dto.ModelDownloadTaskItem -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/hf/download [post] -// @x-panel-log {"bodyKeys":["repoID","revision"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载 Hugging Face 模型 [repoID]","formatEN":"download Hugging Face model [repoID]"} -func (b *BaseApi) StartHFModelDownload(c *gin.Context) { - var req dto.ModelDownload - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - task, err := modelDownloaderService.StartDownload(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, task) -} - -// @Tags AI -// @Summary Search ModelScope models -// @Accept json -// @Param request body dto.ModelSearch true "request" -// @Produce json -// @Success 200 {object} dto.PageResult{items=[]dto.ModelItem} -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/modelscope/search [post] -func (b *BaseApi) SearchModelScopeModels(c *gin.Context) { - var req dto.ModelSearch - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - total, items, err := modelDownloaderService.SearchModelScopeModels(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, dto.PageResult{Total: total, Items: items}) -} - -// @Tags AI -// @Summary Get ModelScope model details -// @Accept json -// @Param request body dto.ModelInfoReq true "request" -// @Produce json -// @Success 200 {object} dto.ModelInfo -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/modelscope/info [post] -func (b *BaseApi) GetModelScopeModelInfo(c *gin.Context) { - var req dto.ModelInfoReq - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - item, err := modelDownloaderService.GetModelScopeModelInfo(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, item) -} - -// @Tags AI -// @Summary Download ModelScope model -// @Description Creates an asynchronous download task. Query /ai/model/downloader/tasks/search for progress. -// @Accept json -// @Param request body dto.ModelDownload true "request" -// @Produce json -// @Success 200 {object} dto.ModelDownloadTaskItem -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/modelscope/download [post] -// @x-panel-log {"bodyKeys":["repoID","revision"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载 ModelScope 模型 [repoID]","formatEN":"download ModelScope model [repoID]"} -func (b *BaseApi) StartModelScopeModelDownload(c *gin.Context) { - var req dto.ModelDownload - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - task, err := modelDownloaderService.StartModelScopeDownload(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, task) -} - -// @Tags AI -// @Summary Get model downloader settings -// @Produce json -// @Success 200 {object} dto.ModelDownloaderSetting -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/settings [get] -func (b *BaseApi) GetModelDownloaderSettings(c *gin.Context) { - settings, err := modelDownloaderService.GetSettings() - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, settings) -} - -// @Tags AI -// @Summary Update model downloader settings -// @Description Updates the download directory, source endpoints and tokens. The featuredModelsSync field is ignored; use /ai/model/downloader/featured/sync/status to update automatic synchronization. -// @Accept json -// @Param request body dto.ModelDownloaderSetting true "request" -// @Produce json -// @Success 200 -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/settings [post] -// @x-panel-log {"bodyKeys":["modelDir"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新模型下载设置 [modelDir]","formatEN":"update model downloader settings [modelDir]"} -func (b *BaseApi) UpdateModelDownloaderSettings(c *gin.Context) { - var req dto.ModelDownloaderSetting - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - if err := modelDownloaderService.UpdateSettings(req); err != nil { - handleModelDownloaderError(c, err) - return - } - helper.Success(c) -} - -// @Tags AI -// @Summary Page model download tasks -// @Description Set includeLocal to true to include locally downloaded models. -// @Accept json -// @Param request body dto.ModelDownloadTaskSearch true "request" -// @Produce json -// @Success 200 {object} dto.PageResult{items=[]dto.ModelDownloadTaskItem} -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/tasks/search [post] -func (b *BaseApi) PageModelDownloadTasks(c *gin.Context) { - var req dto.ModelDownloadTaskSearch - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - total, items, err := modelDownloaderService.PageTasks(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, dto.PageResult{Items: items, Total: total}) -} - -// @Tags AI -// @Summary Cancel model download task -// @Description Only waiting or downloading tasks can be canceled. The withFile field is ignored. -// @Accept json -// @Param request body dto.ModelDownloadTaskOperate true "request" -// @Produce json -// @Success 200 -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/tasks/cancel [post] -// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"model_download_tasks","output_column":"repo_id","output_value":"repoID"}],"formatZH":"取消模型下载任务 [repoID]","formatEN":"cancel model download task [repoID]"} -func (b *BaseApi) CancelModelDownloadTask(c *gin.Context) { - var req dto.ModelDownloadTaskOperate - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - if err := modelDownloaderService.CancelTask(req); err != nil { - handleModelDownloaderError(c, err) - return - } - helper.Success(c) -} - -// @Tags AI -// @Summary Retry model download task -// @Description Only failed or canceled tasks can be retried. Returns the task while the download runs asynchronously. -// @Accept json -// @Param request body dto.ModelDownloadTaskRetry true "request" -// @Produce json -// @Success 200 {object} dto.ModelDownloadTaskItem -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/tasks/retry [post] -// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"model_download_tasks","output_column":"repo_id","output_value":"repoID"}],"formatZH":"重试模型下载任务 [repoID]","formatEN":"retry model download task [repoID]"} -func (b *BaseApi) RetryModelDownloadTask(c *gin.Context) { - var req dto.ModelDownloadTaskRetry - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - task, err := modelDownloaderService.RetryTask(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, task) -} - -// @Tags AI -// @Summary Remove model download task -// @Description Waiting or downloading tasks cannot be removed. Set withFile to true to also delete downloaded files. -// @Accept json -// @Param request body dto.ModelDownloadTaskOperate true "request" -// @Produce json -// @Success 200 -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/tasks/remove [post] -// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"model_download_tasks","output_column":"repo_id","output_value":"repoID"}],"formatZH":"移除模型下载任务 [repoID]","formatEN":"remove model download task [repoID]"} -func (b *BaseApi) RemoveModelDownloadTask(c *gin.Context) { - var req dto.ModelDownloadTaskOperate - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - if err := modelDownloaderService.RemoveTask(req); err != nil { - handleModelDownloaderError(c, err) - return - } - helper.Success(c) -} - -// @Tags AI -// @Summary Page locally downloaded models -// @Accept json -// @Param request body dto.LocalModelSearch true "request" -// @Produce json -// @Success 200 {object} dto.PageResult{items=[]dto.LocalModelItem} -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/local/search [post] -func (b *BaseApi) PageLocalDownloadedModels(c *gin.Context) { - var req dto.LocalModelSearch - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - total, items, err := modelDownloaderService.ListLocalModels(req) - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, dto.PageResult{Items: items, Total: total}) -} - -func (b *BaseApi) ListLocalModelOptions(c *gin.Context) { - items, err := modelDownloaderService.ListLocalModelOptions() - if err != nil { - handleModelDownloaderError(c, err) - return - } - helper.SuccessWithData(c, items) -} - -// @Tags AI -// @Summary Delete locally downloaded model -// @Accept json -// @Param request body dto.LocalModelDelete true "request" -// @Produce json -// @Success 200 -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/local/delete [post] -// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除本地模型 [name]","formatEN":"delete local model [name]"} -func (b *BaseApi) DeleteLocalDownloadedModel(c *gin.Context) { - var req dto.LocalModelDelete - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - if err := modelDownloaderService.DeleteLocalModel(req); err != nil { - handleModelDownloaderError(c, err) - return - } - helper.Success(c) -} - -// @Tags AI -// @Summary Synchronize featured models -// @Description Synchronizes the featured model catalog immediately, independently of the automatic synchronization setting. Does nothing in offline mode. -// @Accept json -// @Produce json -// @Success 200 -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/featured/sync [post] -func (b *BaseApi) SyncFeaturedModels(c *gin.Context) { - if err := modelDownloaderService.SyncFeaturedModels(); err != nil { - helper.InternalServer(c, err) - return - } - helper.Success(c) -} - -// @Tags AI -// @Summary Page featured models -// @Accept json -// @Param request body dto.FeaturedModelSearch true "request" -// @Produce json -// @Success 200 {object} dto.FeaturedModelPage{items=[]dto.FeaturedModel} -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/featured/search [post] -func (b *BaseApi) PageFeaturedModels(c *gin.Context) { - var req dto.FeaturedModelSearch - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - result, err := modelDownloaderService.PageFeaturedModels(req) - if err != nil { - helper.InternalServer(c, err) - return - } - helper.SuccessWithData(c, result) -} - -// @Tags AI -// @Summary Update featured model automatic synchronization -// @Description Set value to Enable or Disable. This updates the automatic synchronization setting without triggering an immediate synchronization. -// @Accept json -// @Param request body dto.ModelDownloaderSyncSetting true "request" -// @Produce json -// @Success 200 -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /ai/model/downloader/featured/sync/status [post] -// @x-panel-log {"bodyKeys":["value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新推荐模型自动同步 [value]","formatEN":"update featured model automatic synchronization [value]"} -func (b *BaseApi) UpdateFeaturedModelsSync(c *gin.Context) { - var req dto.ModelDownloaderSyncSetting - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - if err := modelDownloaderService.UpdateFeaturedModelsSync(req); err != nil { - handleModelDownloaderError(c, err) - return - } - helper.Success(c) -} diff --git a/agent/app/dto/model_downloader.go b/agent/app/dto/model_downloader.go deleted file mode 100644 index 2964a39a1..000000000 --- a/agent/app/dto/model_downloader.go +++ /dev/null @@ -1,153 +0,0 @@ -package dto - -type ModelSearch struct { - Query string `json:"query"` - Sort string `json:"sort" validate:"omitempty,oneof=trending downloads updated created likes most_params least_params"` - Page int `json:"page" validate:"omitempty,min=1"` - PageSize int `json:"pageSize" validate:"omitempty,min=1,max=100"` - Limit int `json:"limit" validate:"omitempty,min=1,max=100"` -} - -type ModelInfoReq struct { - RepoID string `json:"repoID" validate:"required"` - Revision string `json:"revision"` -} - -type ModelDownload struct { - DownloadType string `json:"downloadType" validate:"omitempty,oneof=all tokenizer"` - RepoID string `json:"repoID" validate:"required"` - Revision string `json:"revision"` -} - -type ModelDownloaderSyncSetting struct { - Value string `json:"value" validate:"required,oneof=Enable Disable"` -} - -type ModelDownloaderSetting struct { - FeaturedModelsSync string `json:"featuredModelsSync"` - ModelDir string `json:"modelDir" validate:"required"` - HFEndpoint string `json:"hfEndpoint"` - HFToken string `json:"hfToken"` - ModelScopeEndpoint string `json:"modelScopeEndpoint"` - ModelScopeToken string `json:"modelScopeToken"` -} - -type ModelItem struct { - RepoID string `json:"repoID"` - Name string `json:"name"` - Downloads int64 `json:"downloads"` - Likes int64 `json:"likes"` - Size int64 `json:"size"` - SizeFormatted string `json:"sizeFormatted"` -} - -type ModelFile struct { - Name string `json:"name"` - Size int64 `json:"size"` - SizeFormatted string `json:"sizeFormatted"` -} - -type ModelInfo struct { - ModelItem - ModelCard string `json:"modelCard"` - Files []ModelFile `json:"files"` -} - -type ModelDownloadTaskSearch struct { - IDs []uint `json:"ids"` - IncludeLocal bool `json:"includeLocal"` - DownloadType string `json:"downloadType" validate:"omitempty,oneof=all tokenizer"` - PageInfo - Info string `json:"info"` - Status string `json:"status"` -} - -type ModelDownloadTaskOperate struct { - WithFile bool `json:"withFile"` - ID uint `json:"id" validate:"required"` -} - -type ModelDownloadTaskRetry struct { - ID uint `json:"id" validate:"required"` -} - -type ModelDownloadTaskItem struct { - LocalModel *LocalModelItem `json:"localModel,omitempty"` - DownloadType string `json:"downloadType"` - ID uint `json:"id"` - Source string `json:"source"` - RepoID string `json:"repoID"` - Revision string `json:"revision"` - ModelName string `json:"modelName"` - TargetDir string `json:"targetDir"` - Status string `json:"status"` - Progress float64 `json:"progress"` - TotalSize int64 `json:"totalSize"` - DownloadedSize int64 `json:"downloadedSize"` - ErrorMessage string `json:"errorMessage"` - CreatedAt string `json:"createdAt"` - StartedAt string `json:"startedAt"` - CompletedAt string `json:"completedAt"` - CanCancel bool `json:"canCancel"` - CanRetry bool `json:"canRetry"` - CanRemoveRecord bool `json:"canRemoveRecord"` -} - -type LocalModelOption struct { - Name string `json:"name"` - Organization string `json:"organization"` - Path string `json:"path"` -} - -type LocalModelSearch struct { - DownloadType string `json:"downloadType" validate:"omitempty,oneof=all tokenizer"` - PageInfo - Info string `json:"info"` -} - -type LocalModelItem struct { - DownloadType string `json:"downloadType"` - RepoID string `json:"repoID"` - Source string `json:"source"` - Revision string `json:"revision"` - Name string `json:"name"` - Path string `json:"path"` - Size int64 `json:"size"` - SizeFormatted string `json:"sizeFormatted"` - CreatedAt string `json:"createdAt"` -} - -type LocalModelDelete struct { - WithFile bool `json:"withFile"` - DownloadType string `json:"downloadType" validate:"omitempty,oneof=all tokenizer"` - Name string `json:"name" validate:"required"` -} - -type FeaturedModel struct { - Key string `json:"key" yaml:"key"` - Sort int `json:"sort" yaml:"sort"` - Family string `json:"family" yaml:"category"` - RepoID string `json:"repoID" yaml:"name"` - WeightProvider string `json:"weightProvider" yaml:"weightProvider"` - Sources []string `json:"sources" yaml:"sources"` - SourceModelIDs map[string]string `json:"sourceModelIds" yaml:"sourceModelIds"` - Architecture string `json:"architecture" yaml:"modelType"` - BackboneParams string `json:"backboneParams" yaml:"moeBackboneParameters"` - ActiveParams string `json:"activeParams" yaml:"activeParameters"` - Engram string `json:"engram" yaml:"engramParameters"` - WeightGB float64 `json:"weightGB" yaml:"approximateWeightSizeGB"` - Description map[string]string `json:"description" yaml:"description"` -} - -type FeaturedModelSearch struct { - PageInfo - Family string `json:"family"` - WeightProvider string `json:"weightProvider"` - Info string `json:"info"` -} - -type FeaturedModelPage struct { - PageResult - Families []string `json:"families"` - WeightProviders []string `json:"weightProviders"` -} diff --git a/agent/app/model/featured_model.go b/agent/app/model/featured_model.go deleted file mode 100644 index 37e490f89..000000000 --- a/agent/app/model/featured_model.go +++ /dev/null @@ -1,18 +0,0 @@ -package model - -type FeaturedModel struct { - BaseModel - Key string `json:"key"` - Sort int `json:"sort"` - Family string `json:"family"` - RepoID string `json:"repoID"` - WeightProvider string `json:"weightProvider"` - Sources []string `json:"sources" gorm:"serializer:json"` - SourceModelIDs map[string]string `json:"sourceModelIds" gorm:"serializer:json"` - Architecture string `json:"architecture"` - BackboneParams string `json:"backboneParams"` - ActiveParams string `json:"activeParams"` - Engram string `json:"engram"` - WeightGB float64 `json:"weightGB"` - Description string `json:"description"` -} diff --git a/agent/app/model/model_downloader.go b/agent/app/model/model_downloader.go deleted file mode 100644 index 4333f1fc5..000000000 --- a/agent/app/model/model_downloader.go +++ /dev/null @@ -1,20 +0,0 @@ -package model - -import "time" - -type ModelDownloadTask struct { - DownloadType string `json:"downloadType"` - BaseModel - Source string `json:"source" gorm:"type:varchar(32);not null;index"` - RepoID string `json:"repoID" gorm:"type:varchar(255);not null;index"` - Revision string `json:"revision" gorm:"type:varchar(128)"` - ModelName string `json:"modelName" gorm:"type:varchar(255);not null;index"` - TargetDir string `json:"targetDir" gorm:"type:text"` - Status string `json:"status" gorm:"type:varchar(32);not null;index"` - Progress float64 `json:"progress"` - TotalSize int64 `json:"totalSize"` - DownloadedSize int64 `json:"downloadedSize"` - ErrorMessage string `json:"errorMessage" gorm:"type:text"` - StartedAt *time.Time `json:"startedAt"` - CompletedAt *time.Time `json:"completedAt"` -} diff --git a/agent/app/repo/featured_model.go b/agent/app/repo/featured_model.go deleted file mode 100644 index b3a476c6c..000000000 --- a/agent/app/repo/featured_model.go +++ /dev/null @@ -1,70 +0,0 @@ -package repo - -import ( - "strings" - - "github.com/1Panel-dev/1Panel/agent/app/model" - "github.com/1Panel-dev/1Panel/agent/global" - "gorm.io/gorm" -) - -type FeaturedModelRepo struct{} - -type IFeaturedModelRepo interface { - Page(page, size int, family, weightProvider, info string) (int64, []model.FeaturedModel, error) - Families() ([]string, error) - WeightProviders() ([]string, error) - SyncAll(models []model.FeaturedModel, version string) error -} - -func NewIFeaturedModelRepo() IFeaturedModelRepo { - return &FeaturedModelRepo{} -} - -func (r *FeaturedModelRepo) Page(page, size int, family, weightProvider, info string) (int64, []model.FeaturedModel, error) { - var total int64 - var items []model.FeaturedModel - db := global.DB.Model(&model.FeaturedModel{}) - if family != "" { - db = db.Where("family = ?", family) - } - if weightProvider != "" { - db = db.Where("weight_provider = ?", weightProvider) - } - if info != "" { - db = db.Where("LOWER(repo_id) LIKE ?", "%"+strings.ToLower(info)+"%") - } - if err := db.Count(&total).Error; err != nil { - return 0, nil, err - } - if err := db.Order("sort asc").Order("id asc").Limit(size).Offset(size * (page - 1)).Find(&items).Error; err != nil { - return 0, nil, err - } - return total, items, nil -} - -func (r *FeaturedModelRepo) Families() ([]string, error) { - families := make([]string, 0) - err := global.DB.Model(&model.FeaturedModel{}).Distinct("family").Order("family asc").Pluck("family", &families).Error - return families, err -} - -func (r *FeaturedModelRepo) WeightProviders() ([]string, error) { - providers := make([]string, 0) - err := global.DB.Model(&model.FeaturedModel{}).Where("weight_provider <> ?", "").Distinct("weight_provider").Order("weight_provider asc").Pluck("weight_provider", &providers).Error - return providers, err -} - -func (r *FeaturedModelRepo) SyncAll(models []model.FeaturedModel, version string) error { - return global.DB.Transaction(func(tx *gorm.DB) error { - if err := tx.Where("1 = 1").Delete(&model.FeaturedModel{}).Error; err != nil { - return err - } - if err := tx.Create(&models).Error; err != nil { - return err - } - return tx.Where("key = ?", "FeaturedModelsVersion"). - Assign(model.Setting{Value: version}). - FirstOrCreate(&model.Setting{Key: "FeaturedModelsVersion"}).Error - }) -} diff --git a/agent/app/repo/model_downloader.go b/agent/app/repo/model_downloader.go deleted file mode 100644 index 629eb1d33..000000000 --- a/agent/app/repo/model_downloader.go +++ /dev/null @@ -1,126 +0,0 @@ -package repo - -import ( - "github.com/1Panel-dev/1Panel/agent/app/model" - "github.com/1Panel-dev/1Panel/agent/global" - "gorm.io/gorm" -) - -type ModelDownloadTaskRepo struct{} - -type IModelDownloadTaskRepo interface { - Page(page, size int, info, status string, opts ...DBOption) (int64, []model.ModelDownloadTask, error) - GetFirst(opts ...DBOption) (model.ModelDownloadTask, error) - List(opts ...DBOption) ([]model.ModelDownloadTask, error) - Create(task *model.ModelDownloadTask) error - Update(id uint, values map[string]interface{}) error - UpdateBy(values map[string]interface{}, opts ...DBOption) error - DeleteBy(opts ...DBOption) error - WithByDownloadType(downloadType string) DBOption - WithBySource(source string) DBOption - WithByRepoID(repoID string) DBOption - WithByTargetDir(targetDir string) DBOption - WithByStatusIn(status []string) DBOption -} - -func NewIModelDownloadTaskRepo() IModelDownloadTaskRepo { - return &ModelDownloadTaskRepo{} -} - -func (m *ModelDownloadTaskRepo) WithBySource(source string) DBOption { - return func(g *gorm.DB) *gorm.DB { - return g.Where("source = ?", source) - } -} - -func (m *ModelDownloadTaskRepo) WithByRepoID(repoID string) DBOption { - return func(g *gorm.DB) *gorm.DB { - return g.Where("repo_id = ?", repoID) - } -} - -func (m *ModelDownloadTaskRepo) WithByTargetDir(targetDir string) DBOption { - return func(g *gorm.DB) *gorm.DB { - return g.Where("target_dir = ?", targetDir) - } -} - -func (m *ModelDownloadTaskRepo) WithByStatusIn(status []string) DBOption { - return func(g *gorm.DB) *gorm.DB { - return g.Where("status in ?", status) - } -} - -func (m *ModelDownloadTaskRepo) Page(page, size int, info, status string, opts ...DBOption) (int64, []model.ModelDownloadTask, error) { - var ( - total int64 - list []model.ModelDownloadTask - ) - db := global.DB.Model(&model.ModelDownloadTask{}) - for _, opt := range opts { - db = opt(db) - } - if info != "" { - db = db.Where("repo_id LIKE ? OR model_name LIKE ?", "%"+info+"%", "%"+info+"%") - } - if status != "" { - db = db.Where("status = ?", status) - } - if err := db.Count(&total).Error; err != nil { - return 0, nil, err - } - if err := db.Order("created_at desc").Limit(size).Offset(size * (page - 1)).Find(&list).Error; err != nil { - return 0, nil, err - } - return total, list, nil -} - -func (m *ModelDownloadTaskRepo) GetFirst(opts ...DBOption) (model.ModelDownloadTask, error) { - var item model.ModelDownloadTask - db := global.DB.Model(&model.ModelDownloadTask{}) - for _, opt := range opts { - db = opt(db) - } - err := db.First(&item).Error - return item, err -} - -func (m *ModelDownloadTaskRepo) Create(task *model.ModelDownloadTask) error { - return global.DB.Create(task).Error -} - -func (m *ModelDownloadTaskRepo) List(opts ...DBOption) ([]model.ModelDownloadTask, error) { - var items []model.ModelDownloadTask - db := global.DB.Model(&model.ModelDownloadTask{}) - for _, opt := range opts { - db = opt(db) - } - err := db.Find(&items).Error - return items, err -} - -func (m *ModelDownloadTaskRepo) Update(id uint, values map[string]interface{}) error { - return global.DB.Model(&model.ModelDownloadTask{}).Where("id = ?", id).Updates(values).Error -} - -func (m *ModelDownloadTaskRepo) UpdateBy(values map[string]interface{}, opts ...DBOption) error { - db := global.DB.Model(&model.ModelDownloadTask{}) - for _, opt := range opts { - db = opt(db) - } - return db.Updates(values).Error -} - -func (m *ModelDownloadTaskRepo) DeleteBy(opts ...DBOption) error { - db := global.DB.Model(&model.ModelDownloadTask{}) - for _, opt := range opts { - db = opt(db) - } - return db.Delete(&model.ModelDownloadTask{}).Error -} - -func (m *ModelDownloadTaskRepo) WithByDownloadType(downloadType string) DBOption { - return func(g *gorm.DB) *gorm.DB { - return g.Where("download_type = ?", downloadType) - } -} diff --git a/agent/app/service/entry.go b/agent/app/service/entry.go index 33d02a01f..f3d573e52 100644 --- a/agent/app/service/entry.go +++ b/agent/app/service/entry.go @@ -4,8 +4,6 @@ import "github.com/1Panel-dev/1Panel/agent/app/repo" var ( vmRepo = repo.NewIVMRepo() - modelDownloadTaskRepo = repo.NewIModelDownloadTaskRepo() - featuredModelRepo = repo.NewIFeaturedModelRepo() appRepo = repo.NewIAppRepo() appTagRepo = repo.NewIAppTagRepo() appDetailRepo = repo.NewIAppDetailRepo() diff --git a/agent/app/service/featured_model.go b/agent/app/service/featured_model.go deleted file mode 100644 index 4ed8b8f12..000000000 --- a/agent/app/service/featured_model.go +++ /dev/null @@ -1,132 +0,0 @@ -package service - -import ( - "encoding/json" - "errors" - "fmt" - "strings" - "sync" - - "github.com/1Panel-dev/1Panel/agent/app/dto" - "github.com/1Panel-dev/1Panel/agent/app/model" - "github.com/1Panel-dev/1Panel/agent/constant" - "github.com/1Panel-dev/1Panel/agent/global" - "github.com/1Panel-dev/1Panel/agent/utils/req_helper" - "gopkg.in/yaml.v3" - "gorm.io/gorm" -) - -var featuredModelsSyncMu sync.Mutex - -func (m *ModelDownloaderService) PageFeaturedModels(req dto.FeaturedModelSearch) (*dto.FeaturedModelPage, error) { - total, models, err := featuredModelRepo.Page(req.Page, req.PageSize, strings.TrimSpace(req.Family), strings.TrimSpace(req.WeightProvider), strings.TrimSpace(req.Info)) - if err != nil { - return nil, err - } - items := make([]dto.FeaturedModel, 0, len(models)) - for _, item := range models { - var description map[string]string - if err := json.Unmarshal([]byte(item.Description), &description); err != nil { - return nil, fmt.Errorf("parse featured model %s description failed: %w", item.Key, err) - } - items = append(items, dto.FeaturedModel{ - Key: item.Key, Sort: item.Sort, Family: item.Family, RepoID: item.RepoID, Sources: item.Sources, - SourceModelIDs: item.SourceModelIDs, WeightProvider: item.WeightProvider, - Architecture: item.Architecture, BackboneParams: item.BackboneParams, - ActiveParams: item.ActiveParams, Engram: item.Engram, WeightGB: item.WeightGB, - Description: description, - }) - } - families, err := featuredModelRepo.Families() - if err != nil { - return nil, err - } - weightProviders, err := featuredModelRepo.WeightProviders() - if err != nil { - return nil, err - } - return &dto.FeaturedModelPage{PageResult: dto.PageResult{Total: total, Items: items}, Families: families, WeightProviders: weightProviders}, nil -} - -func (m *ModelDownloaderService) SyncFeaturedModels() error { - if global.CONF.Base.IsOffline { - return nil - } - featuredModelsSyncMu.Lock() - defer featuredModelsSyncMu.Unlock() - localVersion, err := settingRepo.GetValueByKey("FeaturedModelsVersion") - if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { - return err - } - data, version, err := req_helper.LoadAIResource("featured-models", localVersion) - if err != nil || data == nil { - return err - } - var remote struct { - Models []dto.FeaturedModel `yaml:"models"` - } - if err := yaml.Unmarshal(data, &remote); err != nil { - return fmt.Errorf("parse featured models failed: %w", err) - } - if len(remote.Models) == 0 { - return errors.New("featured models data is empty") - } - keys := make(map[string]bool, len(remote.Models)) - models := make([]model.FeaturedModel, 0, len(remote.Models)) - for i := range remote.Models { - item := &remote.Models[i] - item.Key = strings.TrimSpace(item.Key) - item.Family = strings.TrimSpace(item.Family) - item.RepoID = strings.TrimSpace(item.RepoID) - item.WeightProvider = strings.TrimSpace(item.WeightProvider) - if item.WeightProvider == "" { - if provider, _, ok := strings.Cut(item.RepoID, "/"); ok { - item.WeightProvider = provider - } - } - if item.Key == "" || item.Family == "" || item.RepoID == "" || (item.Architecture != "MoE" && item.Architecture != "Dense") || item.WeightGB < 0 { - return fmt.Errorf("invalid featured model: %s", item.Key) - } - if keys[item.Key] { - return fmt.Errorf("duplicate featured model key: %s", item.Key) - } - keys[item.Key] = true - if len(item.Sources) == 0 { - item.Sources = []string{"modelscope"} - } - for _, source := range item.Sources { - if source != "modelscope" && source != "huggingface" { - return fmt.Errorf("invalid featured model %s source: %s", item.Key, source) - } - } - description, err := json.Marshal(item.Description) - if err != nil { - return err - } - models = append(models, model.FeaturedModel{ - Key: item.Key, Sort: item.Sort, Family: item.Family, RepoID: item.RepoID, Sources: item.Sources, - SourceModelIDs: item.SourceModelIDs, WeightProvider: item.WeightProvider, - Architecture: item.Architecture, BackboneParams: item.BackboneParams, - ActiveParams: item.ActiveParams, Engram: item.Engram, WeightGB: item.WeightGB, - Description: string(description), - }) - } - return featuredModelRepo.SyncAll(models, version) -} - -func SyncFeaturedModels() { - if global.CONF.Base.IsOffline { - return - } - status, err := settingRepo.GetValueByKey("FeaturedModelsSync") - if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { - global.LOG.Errorf("load featured model sync setting failed: %v", err) - return - } - if err == nil && status != constant.StatusEnable { - return - } - if err := NewIModelDownloaderService().SyncFeaturedModels(); err != nil { - global.LOG.Errorf("sync featured models failed: %v", err) - } -} diff --git a/agent/app/service/model_downloader.go b/agent/app/service/model_downloader.go deleted file mode 100644 index a35c54045..000000000 --- a/agent/app/service/model_downloader.go +++ /dev/null @@ -1,1524 +0,0 @@ -package service - -import ( - "context" - "crypto/sha256" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "os" - "path/filepath" - "sort" - "strings" - "sync" - "time" - - "github.com/1Panel-dev/1Panel/agent/app/dto" - "github.com/1Panel-dev/1Panel/agent/app/model" - "github.com/1Panel-dev/1Panel/agent/app/repo" - "github.com/1Panel-dev/1Panel/agent/buserr" - "github.com/1Panel-dev/1Panel/agent/constant" - "github.com/1Panel-dev/1Panel/agent/global" - "github.com/1Panel-dev/1Panel/agent/utils/files" - "github.com/1Panel-dev/1Panel/agent/utils/re" - "gorm.io/gorm" -) - -const ( - defaultModelDownloaderDir = "/opt/1panel/ai/models" - defaultHFEndpoint = "https://huggingface.co" - defaultModelScopeEndpoint = "https://www.modelscope.cn" - modelDownloaderSourceHuggingFace = "huggingface" - modelDownloaderSourceModelScope = "modelscope" - statusDownloading = "Downloading" - modelDownloaderDirKey = "ModelDownloaderDir" - modelDownloaderEndpointKey = "ModelDownloaderHFEndpoint" - modelDownloaderTokenKey = "ModelDownloaderHFToken" - modelDownloaderMSEndpointKey = "ModelDownloaderModelScopeEndpoint" - modelDownloaderMSTokenKey = "ModelDownloaderModelScopeToken" -) - -type IModelDownloaderService interface { - PageFeaturedModels(req dto.FeaturedModelSearch) (*dto.FeaturedModelPage, error) - SyncFeaturedModels() error - SearchHFModels(req dto.ModelSearch) (int64, []dto.ModelItem, error) - GetHFModelInfo(req dto.ModelInfoReq) (*dto.ModelInfo, error) - StartDownload(req dto.ModelDownload) (*dto.ModelDownloadTaskItem, error) - SearchModelScopeModels(req dto.ModelSearch) (int64, []dto.ModelItem, error) - GetModelScopeModelInfo(req dto.ModelInfoReq) (*dto.ModelInfo, error) - StartModelScopeDownload(req dto.ModelDownload) (*dto.ModelDownloadTaskItem, error) - GetSettings() (*dto.ModelDownloaderSetting, error) - UpdateSettings(req dto.ModelDownloaderSetting) error - UpdateFeaturedModelsSync(req dto.ModelDownloaderSyncSetting) error - PageTasks(req dto.ModelDownloadTaskSearch) (int64, []dto.ModelDownloadTaskItem, error) - CancelTask(req dto.ModelDownloadTaskOperate) error - RetryTask(req dto.ModelDownloadTaskRetry) (*dto.ModelDownloadTaskItem, error) - RemoveTask(req dto.ModelDownloadTaskOperate) error - RecoverInterruptedTasks() error - ListLocalModels(req dto.LocalModelSearch) (int64, []dto.LocalModelItem, error) - ListLocalModelOptions() ([]dto.LocalModelOption, error) - DeleteLocalModel(req dto.LocalModelDelete) error -} - -type ModelDownloaderService struct { - client *http.Client - downloadClient *http.Client - mu sync.Mutex - workers map[uint]*modelDownloadWorker - downloadSem chan struct{} -} - -type modelDownloadWorker struct { - cancel context.CancelFunc - done chan struct{} -} - -type modelDownloaderInputError struct { - err error -} - -func (m modelDownloaderInputError) Error() string { - return m.err.Error() -} - -func (m modelDownloaderInputError) Unwrap() error { - return m.err -} - -func newModelDownloaderInputError(format string, args ...interface{}) error { - return modelDownloaderInputError{err: fmt.Errorf(format, args...)} -} - -func newModelDownloaderRequestFailed(err error) error { - if err == nil { - err = errors.New("request failed") - } - if err.Error() == "" { - err = errors.New("request failed") - } - return buserr.WithMap("ErrHttpReqFailed", map[string]interface{}{"err": err.Error()}, err) -} - -func IsModelDownloaderInputError(err error) bool { - var target modelDownloaderInputError - return errors.As(err, &target) -} - -func NewIModelDownloaderService() IModelDownloaderService { - return &ModelDownloaderService{ - client: &http.Client{Timeout: 30 * time.Second}, - downloadClient: &http.Client{}, - workers: make(map[uint]*modelDownloadWorker), - downloadSem: make(chan struct{}, 1), - } -} - -func (m *ModelDownloaderService) SearchHFModels(req dto.ModelSearch) (int64, []dto.ModelItem, error) { - normalizeModelSearch(&req) - - var raw []hfModelResp - if err := m.getJSON(context.Background(), buildHFModelSearchURL(hfEndpoint(), req), loadModelDownloaderHFToken(), &raw); err != nil { - return 0, nil, err - } - items := make([]dto.ModelItem, 0, len(raw)) - for _, item := range raw { - items = append(items, mapHFModel(item)) - } - return estimateHFSearchTotal(req, len(items)), items, nil -} - -func (m *ModelDownloaderService) GetHFModelInfo(req dto.ModelInfoReq) (*dto.ModelInfo, error) { - repoID, err := normalizeModelDownloaderRepoID(req.RepoID) - if err != nil { - return nil, err - } - return m.loadHFModelInfo(context.Background(), repoID, req.Revision, loadModelDownloaderHFToken()) -} - -func (m *ModelDownloaderService) StartDownload(req dto.ModelDownload) (*dto.ModelDownloadTaskItem, error) { - return m.startDownload(modelDownloaderSourceHuggingFace, req) -} - -func (m *ModelDownloaderService) SearchModelScopeModels(req dto.ModelSearch) (int64, []dto.ModelItem, error) { - normalizeModelSearch(&req) - query := url.Values{} - query.Set("page_number", fmt.Sprintf("%d", req.Page)) - query.Set("page_size", fmt.Sprintf("%d", req.PageSize)) - query.Set("sort", mapModelScopeSort(req.Sort)) - if keyword := strings.TrimSpace(req.Query); keyword != "" { - query.Set("search", keyword) - } - - var raw modelScopeSearchResp - if err := m.getModelScopeOpenJSON(context.Background(), modelScopeEndpoint()+"/openapi/v1/models?"+query.Encode(), loadModelDownloaderModelScopeToken(), &raw); err != nil { - return 0, nil, err - } - if !raw.Success && raw.Message != "" { - return 0, nil, newModelDownloaderRequestFailed(errors.New(raw.Message)) - } - items := make([]dto.ModelItem, 0, len(raw.Data.Models)) - for _, item := range raw.Data.Models { - items = append(items, mapModelScopeModel(item)) - } - return raw.Data.TotalCount, items, nil -} - -func (m *ModelDownloaderService) GetModelScopeModelInfo(req dto.ModelInfoReq) (*dto.ModelInfo, error) { - repoID, err := normalizeModelDownloaderRepoID(req.RepoID) - if err != nil { - return nil, err - } - return m.loadModelScopeModelInfo(context.Background(), repoID, req.Revision, loadModelDownloaderModelScopeToken()) -} - -func (m *ModelDownloaderService) StartModelScopeDownload(req dto.ModelDownload) (*dto.ModelDownloadTaskItem, error) { - return m.startDownload(modelDownloaderSourceModelScope, req) -} - -func (m *ModelDownloaderService) startDownload(source string, req dto.ModelDownload) (*dto.ModelDownloadTaskItem, error) { - repoID, err := normalizeModelDownloaderRepoID(req.RepoID) - if err != nil { - return nil, err - } - if req.DownloadType == "" { - req.DownloadType = "all" - } - if req.DownloadType != "all" && req.DownloadType != "tokenizer" { - return nil, newModelDownloaderInputError("invalid download type") - } - modelName := modelNameFromRepo(repoID) - targetDir := filepath.Join(loadModelDownloaderDir(), repoID) - if req.DownloadType == "tokenizer" { - digest := sha256.Sum256([]byte(source + "\x00" + repoID + "\x00" + resolveRevision(source, req.Revision))) - targetDir = filepath.Join(global.CONF.Base.InstallDir, "1panel/ai/tokenizer", fmt.Sprintf("%s-%x", repoID, digest[:3])) - modelName = filepath.Base(targetDir) - } - m.mu.Lock() - if err := ensureTargetDirAvailable(targetDir); err != nil { - m.mu.Unlock() - return nil, err - } - if err := m.ensureNoActiveDownload(source, repoID, targetDir, req.DownloadType); err != nil { - m.mu.Unlock() - return nil, err - } - task := &model.ModelDownloadTask{ - DownloadType: req.DownloadType, - Source: source, - RepoID: repoID, - Revision: resolveRevision(source, req.Revision), - ModelName: modelName, - TargetDir: targetDir, - Status: constant.StatusWaiting, - } - if err := modelDownloadTaskRepo.Create(task); err != nil { - m.mu.Unlock() - return nil, err - } - m.startTaskLocked(task.ID) - m.mu.Unlock() - return mapDownloadTask(*task), nil -} - -func (m *ModelDownloaderService) GetSettings() (*dto.ModelDownloaderSetting, error) { - syncStatus, err := settingRepo.GetValueByKey("FeaturedModelsSync") - if errors.Is(err, gorm.ErrRecordNotFound) { - syncStatus = constant.StatusEnable - } else if err != nil { - return nil, err - } - return &dto.ModelDownloaderSetting{ - FeaturedModelsSync: syncStatus, - ModelDir: loadModelDownloaderDir(), - HFEndpoint: loadModelDownloaderHFEndpointSetting(), - HFToken: loadModelDownloaderHFToken(), - ModelScopeEndpoint: modelScopeEndpoint(), - ModelScopeToken: loadModelDownloaderModelScopeToken(), - }, nil -} - -func (m *ModelDownloaderService) UpdateSettings(req dto.ModelDownloaderSetting) error { - if err := settingRepo.UpdateOrCreate(modelDownloaderDirKey, resolveDownloaderModelDir(req.ModelDir)); err != nil { - return err - } - if err := updateModelDownloaderEndpointSetting(modelDownloaderEndpointKey, req.HFEndpoint); err != nil { - return err - } - if err := updateModelDownloaderSetting(modelDownloaderTokenKey, req.HFToken); err != nil { - return err - } - if err := updateModelDownloaderEndpointSetting(modelDownloaderMSEndpointKey, req.ModelScopeEndpoint); err != nil { - return err - } - return updateModelDownloaderSetting(modelDownloaderMSTokenKey, req.ModelScopeToken) -} - -func (m *ModelDownloaderService) UpdateFeaturedModelsSync(req dto.ModelDownloaderSyncSetting) error { - if req.Value != constant.StatusEnable && req.Value != constant.StatusDisable { - return newModelDownloaderInputError("invalid sync setting") - } - return settingRepo.UpdateOrCreate("FeaturedModelsSync", req.Value) -} - -func (m *ModelDownloaderService) PageTasks(req dto.ModelDownloadTaskSearch) (int64, []dto.ModelDownloadTaskItem, error) { - if req.IncludeLocal { - return pageModelDownloads(req) - } - var opts []repo.DBOption - if len(req.IDs) > 0 { - opts = append(opts, repo.WithByIDs(req.IDs)) - } - if req.DownloadType != "" { - opts = append(opts, modelDownloadTaskRepo.WithByDownloadType(req.DownloadType)) - } - total, list, err := modelDownloadTaskRepo.Page(req.Page, req.PageSize, req.Info, req.Status, opts...) - if err != nil { - return 0, nil, err - } - items := make([]dto.ModelDownloadTaskItem, 0, len(list)) - for _, item := range list { - items = append(items, *mapDownloadTask(item)) - } - return total, items, nil -} - -func (m *ModelDownloaderService) CancelTask(req dto.ModelDownloadTaskOperate) error { - m.mu.Lock() - defer m.mu.Unlock() - task, err := modelDownloadTaskRepo.GetFirst(repo.WithByID(req.ID)) - if err != nil { - return err - } - if task.Status != constant.StatusWaiting && task.Status != statusDownloading { - return newModelDownloaderInputError("task is not cancellable") - } - if worker := m.workers[req.ID]; worker != nil { - worker.cancel() - } - now := time.Now() - return modelDownloadTaskRepo.Update(req.ID, map[string]interface{}{ - "status": constant.StatusCanceled, - "completed_at": &now, - }) -} - -func (m *ModelDownloaderService) RetryTask(req dto.ModelDownloadTaskRetry) (*dto.ModelDownloadTaskItem, error) { - m.mu.Lock() - defer m.mu.Unlock() - var task model.ModelDownloadTask - for { - var err error - task, err = modelDownloadTaskRepo.GetFirst(repo.WithByID(req.ID)) - if err != nil { - return nil, err - } - if task.Status != constant.StatusFailed && task.Status != constant.StatusCanceled { - return nil, newModelDownloaderInputError("task is not retryable") - } - worker := m.workers[req.ID] - if worker == nil { - break - } - m.mu.Unlock() - <-worker.done - m.mu.Lock() - } - if err := ensureTargetDirAvailable(task.TargetDir); err != nil { - return nil, err - } - if err := m.ensureNoActiveDownload(task.Source, task.RepoID, task.TargetDir, task.DownloadType); err != nil { - return nil, err - } - if err := modelDownloadTaskRepo.Update(task.ID, map[string]interface{}{ - "status": constant.StatusWaiting, - "progress": 0, - "downloaded_size": int64(0), - "error_message": "", - "started_at": nil, - "completed_at": nil, - }); err != nil { - return nil, err - } - m.startTaskLocked(task.ID) - task.Status = constant.StatusWaiting - task.Progress = 0 - task.DownloadedSize = 0 - task.ErrorMessage = "" - task.StartedAt = nil - task.CompletedAt = nil - return mapDownloadTask(task), nil -} - -func (m *ModelDownloaderService) RemoveTask(req dto.ModelDownloadTaskOperate) error { - task, err := modelDownloadTaskRepo.GetFirst(repo.WithByID(req.ID)) - if err != nil { - return err - } - if task.Status == constant.StatusWaiting || task.Status == statusDownloading { - return newModelDownloaderInputError("active task cannot be removed") - } - if req.WithFile { - if err := m.deleteLocalModel(task.TargetDir, task.DownloadType, true); err != nil { - return err - } - if task.DownloadType == "tokenizer" { - return nil - } - } - if err := os.RemoveAll(taskTempDir(task)); err != nil { - return err - } - return modelDownloadTaskRepo.DeleteBy(repo.WithByID(req.ID)) -} - -func (m *ModelDownloaderService) RecoverInterruptedTasks() error { - now := time.Now() - return modelDownloadTaskRepo.UpdateBy( - map[string]interface{}{ - "status": constant.StatusFailed, - "error_message": constant.InterruptedMsg, - "completed_at": &now, - }, - modelDownloadTaskRepo.WithByStatusIn([]string{constant.StatusWaiting, statusDownloading}), - ) -} - -func (m *ModelDownloaderService) ListLocalModels(req dto.LocalModelSearch) (int64, []dto.LocalModelItem, error) { - items, err := listLocalModels(req) - if err != nil { - return 0, nil, err - } - sort.Slice(items, func(i, j int) bool { - return items[i].CreatedAt > items[j].CreatedAt - }) - return int64(len(items)), pageLocalModels(items, req.Page, req.PageSize), nil -} - -func (m *ModelDownloaderService) ListLocalModelOptions() ([]dto.LocalModelOption, error) { - return listLocalModelOptions(loadModelDownloaderDir()) -} - -func (m *ModelDownloaderService) DeleteLocalModel(req dto.LocalModelDelete) error { - if req.Name != strings.TrimSpace(req.Name) { - return newModelDownloaderInputError("invalid model directory") - } - if _, err := normalizeModelDownloaderRepoID(req.Name); err != nil { - return err - } - root := loadModelDownloaderDir() - if req.DownloadType == "tokenizer" { - root = filepath.Join(global.CONF.Base.InstallDir, "1panel/ai/tokenizer") - } - items, err := listModelDownloadDirectories(root) - if err != nil { - return err - } - for _, item := range items { - if item.Name == req.Name { - return m.deleteLocalModel(item.Path, req.DownloadType, req.WithFile) - } - } - return nil -} - -func (m *ModelDownloaderService) deleteLocalModel(target, downloadType string, withFile bool) error { - m.mu.Lock() - defer m.mu.Unlock() - target = filepath.Clean(target) - if target == "." || target == ".." || target == string(filepath.Separator) { - return newModelDownloaderInputError("invalid model directory") - } - for _, path := range []string{filepath.Dir(target), target} { - stat, err := os.Lstat(path) - if err != nil && !os.IsNotExist(err) { - return err - } - if err == nil && (!stat.IsDir() || stat.Mode()&os.ModeSymlink != 0) { - return newModelDownloaderInputError("invalid model directory") - } - } - tasks, err := modelDownloadTaskRepo.List(modelDownloadTaskRepo.WithByTargetDir(target)) - if err != nil { - return err - } - for _, task := range tasks { - if task.Status == constant.StatusWaiting || task.Status == statusDownloading { - return newModelDownloaderInputError("download target directory is still in use by an active download") - } - } - if downloadType != "tokenizer" { - return os.RemoveAll(target) - } - for _, task := range tasks { - if task.DownloadType != "tokenizer" { - continue - } - if err := os.RemoveAll(taskTempDir(task)); err != nil { - return err - } - } - if withFile { - if err := os.RemoveAll(target); err != nil { - return err - } - } else if err := os.Remove(filepath.Join(target, ".1panel-tokenizer")); err != nil && !os.IsNotExist(err) { - return err - } - return modelDownloadTaskRepo.DeleteBy(modelDownloadTaskRepo.WithByTargetDir(target), modelDownloadTaskRepo.WithByDownloadType("tokenizer")) -} - -func pageModelDownloads(req dto.ModelDownloadTaskSearch) (int64, []dto.ModelDownloadTaskItem, error) { - var opts []repo.DBOption - if len(req.IDs) > 0 { - opts = append(opts, repo.WithByIDs(req.IDs)) - } - if req.DownloadType != "" { - opts = append(opts, modelDownloadTaskRepo.WithByDownloadType(req.DownloadType)) - } - tasks, err := modelDownloadTaskRepo.List(opts...) - if err != nil { - return 0, nil, err - } - locals, err := listLocalModels(dto.LocalModelSearch{DownloadType: req.DownloadType}) - if err != nil { - return 0, nil, err - } - localByPath := make(map[string]dto.LocalModelItem, len(locals)) - for _, item := range locals { - localByPath[item.DownloadType+":"+filepath.Clean(item.Path)] = item - } - sort.Slice(tasks, func(i, j int) bool { - iActive := tasks[i].Status == constant.StatusWaiting || tasks[i].Status == statusDownloading - jActive := tasks[j].Status == constant.StatusWaiting || tasks[j].Status == statusDownloading - if iActive != jActive { - return iActive - } - return tasks[i].ID > tasks[j].ID - }) - items := make([]dto.ModelDownloadTaskItem, 0, len(tasks)+len(locals)) - for _, task := range tasks { - item := *mapDownloadTask(task) - key := task.DownloadType + ":" + filepath.Clean(task.TargetDir) - if local, ok := localByPath[key]; ok { - item.LocalModel = &local - delete(localByPath, key) - } - items = append(items, item) - } - if len(req.IDs) == 0 { - for _, local := range localByPath { - repoID := local.RepoID - if repoID == "" { - repoID = local.Name - } - items = append(items, dto.ModelDownloadTaskItem{ - DownloadType: local.DownloadType, - Source: local.Source, - RepoID: repoID, - TargetDir: local.Path, - Status: constant.StatusSuccess, - Progress: 100, - TotalSize: local.Size, - DownloadedSize: local.Size, - CreatedAt: local.CreatedAt, - LocalModel: &local, - }) - } - } - - filtered := items[:0] - for _, item := range items { - if req.Status != "" && item.Status != req.Status { - continue - } - if req.Info != "" && !strings.Contains(strings.ToLower(item.RepoID), strings.ToLower(req.Info)) { - continue - } - filtered = append(filtered, item) - } - sort.Slice(filtered, func(i, j int) bool { - if filtered[i].CreatedAt != filtered[j].CreatedAt { - return filtered[i].CreatedAt > filtered[j].CreatedAt - } - if filtered[i].ID != filtered[j].ID { - return filtered[i].ID > filtered[j].ID - } - return filtered[i].TargetDir < filtered[j].TargetDir - }) - total := int64(len(filtered)) - page, size := req.Page, req.PageSize - if page < 1 { - page = 1 - } - if size < 1 { - size = 20 - } - start := (page - 1) * size - if start >= len(filtered) { - return total, []dto.ModelDownloadTaskItem{}, nil - } - end := start + size - if end > len(filtered) { - end = len(filtered) - } - return total, filtered[start:end], nil -} - -func listLocalModels(req dto.LocalModelSearch) ([]dto.LocalModelItem, error) { - items := make([]dto.LocalModelItem, 0) - if req.DownloadType != "all" { - tokenizers, err := listDownloadedTokenizers(req.Info) - if err != nil { - return nil, err - } - items = append(items, tokenizers...) - } - if req.DownloadType != "tokenizer" { - models, err := listModelDownloadDirectories(loadModelDownloaderDir()) - if err != nil { - return nil, err - } - for _, item := range models { - if req.Info != "" && !strings.Contains(strings.ToLower(item.Name), strings.ToLower(req.Info)) { - continue - } - info, err := os.Stat(item.Path) - if err != nil { - continue - } - item.DownloadType = "all" - item.Size, _ = files.NewFileOp().GetDirSize(item.Path) - item.SizeFormatted = formatBytes(item.Size) - item.CreatedAt = info.ModTime().Format("2006-01-02 15:04:05") - items = append(items, item) - } - } - - return items, nil -} - -func (m *ModelDownloaderService) ensureNoActiveDownload(source, repoID, targetDir, downloadType string) error { - _, err := modelDownloadTaskRepo.GetFirst( - modelDownloadTaskRepo.WithBySource(source), - modelDownloadTaskRepo.WithByRepoID(repoID), - modelDownloadTaskRepo.WithByDownloadType(downloadType), - modelDownloadTaskRepo.WithByStatusIn([]string{constant.StatusWaiting, statusDownloading}), - ) - if err == nil { - return newModelDownloaderInputError("download for %s is already in progress", repoID) - } - if !errors.Is(err, gorm.ErrRecordNotFound) { - return err - } - _, err = modelDownloadTaskRepo.GetFirst( - modelDownloadTaskRepo.WithByTargetDir(targetDir), - modelDownloadTaskRepo.WithByStatusIn([]string{constant.StatusWaiting, statusDownloading}), - ) - if err == nil { - return newModelDownloaderInputError("download target directory is already in progress: %s", targetDir) - } - if !errors.Is(err, gorm.ErrRecordNotFound) { - return err - } - return nil -} - -func (m *ModelDownloaderService) startTaskLocked(taskID uint) { - ctx, cancel := context.WithCancel(context.Background()) - worker := &modelDownloadWorker{cancel: cancel, done: make(chan struct{})} - m.workers[taskID] = worker - go func() { - defer func() { - cancel() - m.mu.Lock() - delete(m.workers, taskID) - close(worker.done) - m.mu.Unlock() - }() - m.downloadTask(ctx, taskID) - }() -} - -func (m *ModelDownloaderService) downloadTask(ctx context.Context, taskID uint) { - select { - case m.downloadSem <- struct{}{}: - defer func() { <-m.downloadSem }() - case <-ctx.Done(): - return - } - task, err := modelDownloadTaskRepo.GetFirst(repo.WithByID(taskID)) - if err != nil { - return - } - if ctx.Err() != nil { - return - } - now := time.Now() - _ = modelDownloadTaskRepo.Update(task.ID, map[string]interface{}{ - "status": statusDownloading, - "started_at": &now, - }) - - if err := m.runDownload(ctx, task); err != nil { - if ctx.Err() != nil { - done := time.Now() - _ = modelDownloadTaskRepo.Update(task.ID, map[string]interface{}{ - "status": constant.StatusCanceled, - "completed_at": &done, - }) - return - } - done := time.Now() - _ = modelDownloadTaskRepo.Update(task.ID, map[string]interface{}{ - "status": constant.StatusFailed, - "error_message": err.Error(), - "completed_at": &done, - }) - } -} - -func (m *ModelDownloaderService) runDownload(ctx context.Context, task model.ModelDownloadTask) error { - info, err := m.loadModelInfo(ctx, task) - if err != nil { - return err - } - var downloadFiles []dto.ModelFile - if task.DownloadType == "tokenizer" { - downloadFiles = filterTokenizerDownloadFiles(info.Files) - if err := validateTokenizerDownloadFiles(downloadFiles); err != nil { - return err - } - } else { - downloadFiles = filterModelDownloadFiles(info.Files) - } - if len(downloadFiles) == 0 { - return newModelDownloaderInputError("no downloadable model files found: %s", task.RepoID) - } - totalSize := int64(0) - for _, item := range downloadFiles { - totalSize += item.Size - } - if err := os.MkdirAll(filepath.Dir(task.TargetDir), 0755); err != nil { - return err - } - if _, err := os.Stat(task.TargetDir); err == nil { - return newModelDownloaderInputError("model directory already exists: %s", task.TargetDir) - } - tmpDir := taskTempDir(task) - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return err - } - downloaded := int64(0) - for _, file := range downloadFiles { - if ctx.Err() != nil { - return ctx.Err() - } - if err := m.downloadModelFile(ctx, task, file, tmpDir, &downloaded, totalSize); err != nil { - return err - } - } - if task.DownloadType == "tokenizer" { - if err := validateDownloadedTokenizer(tmpDir); err != nil { - return err - } - metadata, err := json.Marshal(tokenizerDownloadMetadata{Source: task.Source, RepoID: task.RepoID, Revision: task.Revision}) - if err != nil { - return err - } - if err := os.WriteFile(filepath.Join(tmpDir, ".1panel-tokenizer"), metadata, 0600); err != nil { - return err - } - totalSize = downloaded - } - if err := os.Rename(tmpDir, task.TargetDir); err != nil { - return err - } - done := time.Now() - return modelDownloadTaskRepo.Update(task.ID, map[string]interface{}{ - "status": constant.StatusSuccess, - "progress": float64(100), - "total_size": totalSize, - "downloaded_size": totalSize, - "completed_at": &done, - "error_message": "", - }) -} - -func (m *ModelDownloaderService) loadModelInfo(ctx context.Context, task model.ModelDownloadTask) (*dto.ModelInfo, error) { - switch task.Source { - case modelDownloaderSourceHuggingFace: - return m.loadHFModelInfo(ctx, task.RepoID, task.Revision, loadModelDownloaderHFToken()) - case modelDownloaderSourceModelScope: - return m.loadModelScopeModelInfo(ctx, task.RepoID, task.Revision, loadModelDownloaderModelScopeToken()) - default: - return nil, newModelDownloaderInputError("unsupported model source: %s", task.Source) - } -} - -func (m *ModelDownloaderService) downloadModelFile(ctx context.Context, task model.ModelDownloadTask, file dto.ModelFile, tmpDir string, downloaded *int64, totalSize int64) error { - target, err := safeJoinModelFile(tmpDir, file.Name) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil { - return err - } - existing := int64(0) - if stat, err := os.Stat(target); err == nil { - existing = stat.Size() - } - if task.DownloadType == "tokenizer" && existing > tokenizerDownloadMaxSize-*downloaded { - return newModelDownloaderInputError("tokenizer files exceed 256 MiB") - } - if file.Size > 0 && existing == file.Size { - *downloaded += existing - return m.updateProgress(task.ID, *downloaded, totalSize) - } - fileURL, err := modelFileResolveURL(task.Source, task.RepoID, task.Revision, file.Name) - if err != nil { - return err - } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileURL, nil) - if err != nil { - return err - } - applyDownloadAuth(req, task.Source) - if existing > 0 && (file.Size == 0 || existing < file.Size) { - req.Header.Set("Range", fmt.Sprintf("bytes=%d-", existing)) - } - resp, err := m.downloadClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent { - return fmt.Errorf("download %s failed: %s", file.Name, resp.Status) - } - flag := os.O_CREATE | os.O_WRONLY - if resp.StatusCode == http.StatusPartialContent { - flag |= os.O_APPEND - *downloaded += existing - } else { - flag |= os.O_TRUNC - } - out, err := os.OpenFile(target, flag, 0644) - if err != nil { - return err - } - defer out.Close() - writer := &downloadProgressWriter{ - writer: out, - onProgress: func(n int64) { - *downloaded += n - _ = m.updateProgress(task.ID, *downloaded, totalSize) - }, - } - var input io.Reader = resp.Body - if task.DownloadType == "tokenizer" { - remaining := tokenizerDownloadMaxSize - *downloaded - if remaining < 0 || resp.ContentLength > remaining { - return newModelDownloaderInputError("tokenizer files exceed 256 MiB") - } - input = io.LimitReader(resp.Body, remaining+1) - } - _, err = io.Copy(writer, input) - if writer.updated > 0 { - writer.onProgress(writer.updated) - writer.updated = 0 - } - if task.DownloadType == "tokenizer" && *downloaded > tokenizerDownloadMaxSize { - return newModelDownloaderInputError("tokenizer files exceed 256 MiB") - } - return err -} - -func (m *ModelDownloaderService) updateProgress(taskID uint, downloadedSize, totalSize int64) error { - progress := float64(0) - if totalSize > 0 { - progress = float64(downloadedSize) / float64(totalSize) * 100 - if progress > 99 { - progress = 99 - } - } - return modelDownloadTaskRepo.Update(taskID, map[string]interface{}{ - "total_size": totalSize, - "downloaded_size": downloadedSize, - "progress": progress, - }) -} - -func (m *ModelDownloaderService) loadHFModelInfo(ctx context.Context, repoID, revision, hfToken string) (*dto.ModelInfo, error) { - var raw hfModelResp - if err := m.getJSON(ctx, hfModelInfoURL(repoID, revision), hfToken, &raw); err != nil { - return nil, err - } - item := mapHFModel(raw) - filesList := make([]dto.ModelFile, 0, len(raw.Siblings)) - totalSize := int64(0) - for _, sibling := range raw.Siblings { - if sibling.RFilename == "" { - continue - } - size := sibling.Size - totalSize += size - filesList = append(filesList, dto.ModelFile{ - Name: sibling.RFilename, - Size: size, - SizeFormatted: formatBytes(size), - }) - } - if item.Size == 0 { - item.Size = totalSize - item.SizeFormatted = formatBytes(totalSize) - } - card := m.loadHFModelCard(ctx, repoID, resolveRevision(modelDownloaderSourceHuggingFace, revision), hfToken) - return &dto.ModelInfo{ - ModelItem: item, - ModelCard: card, - Files: filesList, - }, nil -} - -func (m *ModelDownloaderService) loadModelScopeModelInfo(ctx context.Context, repoID, revision, token string) (*dto.ModelInfo, error) { - revision = resolveRevision(modelDownloaderSourceModelScope, revision) - var detail modelScopeDetailResp - detailURL := modelScopeEndpoint() + "/api/v1/models/" + escapedRepoPath(repoID) + "?Revision=" + url.QueryEscape(revision) - if err := m.getModelScopeAPIJSON(ctx, detailURL, token, &detail); err != nil { - return nil, err - } - if detail.Code != 0 && detail.Code != http.StatusOK { - return nil, newModelDownloaderRequestFailed(errors.New(detail.Message)) - } - - var fileResp modelScopeFileListResp - fileURL := modelScopeEndpoint() + "/api/v1/models/" + escapedRepoPath(repoID) + "/repo/files?Revision=" + url.QueryEscape(revision) + "&Recursive=true" - if err := m.getModelScopeAPIJSON(ctx, fileURL, token, &fileResp); err != nil { - return nil, err - } - if fileResp.Code != 0 && fileResp.Code != http.StatusOK { - return nil, newModelDownloaderRequestFailed(errors.New(fileResp.Message)) - } - - filesList := make([]dto.ModelFile, 0, len(fileResp.Data.Files)) - totalSize := int64(0) - for _, file := range fileResp.Data.Files { - if file.Path == "" || (file.Type != "" && file.Type != "blob") { - continue - } - name := strings.TrimLeft(file.Path, "/") - if name == ".gitattributes" || name == ".gitignore" { - continue - } - totalSize += file.Size - filesList = append(filesList, dto.ModelFile{ - Name: name, - Size: file.Size, - SizeFormatted: formatBytes(file.Size), - }) - } - - item := mapModelScopeModel(modelScopeModelResp{ - ID: repoID, - Downloads: detail.Data.Downloads, - Likes: detail.Data.Stars, - FileSize: totalSize, - DisplayName: detail.Data.Name, - }) - item.Size = totalSize - item.SizeFormatted = formatBytes(totalSize) - return &dto.ModelInfo{ - ModelItem: item, - ModelCard: strings.TrimSpace(detail.Data.ReadMeContent), - Files: filesList, - }, nil -} - -func (m *ModelDownloaderService) loadHFModelCard(ctx context.Context, repoID, revision, hfToken string) string { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, hfResolveURL(repoID, revision, "README.md"), nil) - if err != nil { - return "" - } - if hfToken != "" { - req.Header.Set("Authorization", "Bearer "+hfToken) - } - resp, err := m.client.Do(req) - if err != nil { - return "" - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return "" - } - data, err := io.ReadAll(io.LimitReader(resp.Body, 512*1024)) - if err != nil { - return "" - } - card := string(data) - if strings.HasPrefix(card, "---") { - if index := strings.Index(card[3:], "---"); index >= 0 { - card = strings.TrimSpace(card[index+6:]) - } - } - return card -} - -func (m *ModelDownloaderService) getJSON(ctx context.Context, target, token string, out interface{}) error { - return m.getModelDownloaderJSON(ctx, target, out, func(req *http.Request) { - if token != "" { - req.Header.Set("Authorization", "Bearer "+token) - } - }) -} - -func (m *ModelDownloaderService) getModelScopeOpenJSON(ctx context.Context, target, token string, out interface{}) error { - return m.getModelDownloaderJSON(ctx, target, out, func(req *http.Request) { - if token != "" { - req.Header.Set("Authorization", "Bearer "+token) - } - }) -} - -func (m *ModelDownloaderService) getModelScopeAPIJSON(ctx context.Context, target, token string, out interface{}) error { - return m.getModelDownloaderJSON(ctx, target, out, func(req *http.Request) { - applyModelScopeAPIToken(req, token) - }) -} - -func (m *ModelDownloaderService) getModelDownloaderJSON(ctx context.Context, target string, out interface{}, configure func(*http.Request)) error { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, target, nil) - if err != nil { - return err - } - if configure != nil { - configure(req) - } - resp, err := m.client.Do(req) - if err != nil { - return newModelDownloaderRequestFailed(err) - } - defer resp.Body.Close() - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return newModelDownloaderRequestFailed(errors.New(resp.Status)) - } - return json.NewDecoder(resp.Body).Decode(out) -} - -type downloadProgressWriter struct { - writer io.Writer - updated int64 - onProgress func(int64) -} - -func (d *downloadProgressWriter) Write(p []byte) (int, error) { - n, err := d.writer.Write(p) - if n > 0 { - d.updated += int64(n) - if d.updated >= 2*1024*1024 { - d.onProgress(d.updated) - d.updated = 0 - } - } - return n, err -} - -type hfModelResp struct { - ID string `json:"id"` - ModelID string `json:"modelId"` - Downloads int64 `json:"downloads"` - Likes int64 `json:"likes"` - Safetensors hfSafetensors `json:"safetensors"` - Siblings []struct { - RFilename string `json:"rfilename"` - Size int64 `json:"size"` - } `json:"siblings"` -} - -type hfSafetensors struct { - Parameters map[string]float64 `json:"parameters"` -} - -type modelScopeSearchResp struct { - Success bool `json:"success"` - Message string `json:"message"` - Data struct { - Models []modelScopeModelResp `json:"models"` - TotalCount int64 `json:"total_count"` - } `json:"data"` -} - -type modelScopeModelResp struct { - ID string `json:"id"` - Name string `json:"name"` - ModelID string `json:"model_id"` - DisplayName string `json:"display_name"` - Downloads int64 `json:"downloads"` - Likes int64 `json:"likes"` - FileSize int64 `json:"file_size"` - Params int64 `json:"params"` -} - -type modelScopeDetailResp struct { - Code int `json:"Code"` - Message string `json:"Message"` - Data struct { - Name string `json:"Name"` - Downloads int64 `json:"Downloads"` - Stars int64 `json:"Stars"` - ReadMeContent string `json:"ReadMeContent"` - } `json:"Data"` -} - -type modelScopeFileListResp struct { - Code int `json:"Code"` - Message string `json:"Message"` - Data struct { - Files []modelScopeFileResp `json:"Files"` - } `json:"Data"` -} - -type modelScopeFileResp struct { - Path string `json:"Path"` - Type string `json:"Type"` - Size int64 `json:"Size"` -} - -func listModelDownloadDirectories(root string) ([]dto.LocalModelItem, error) { - entries, err := os.ReadDir(root) - if os.IsNotExist(err) { - return []dto.LocalModelItem{}, nil - } - if err != nil { - return nil, err - } - var items []dto.LocalModelItem - for _, entry := range entries { - if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") { - continue - } - children, err := os.ReadDir(filepath.Join(root, entry.Name())) - if err != nil { - return nil, err - } - for _, child := range children { - if child.IsDir() && !strings.HasPrefix(child.Name(), ".") { - items = append(items, dto.LocalModelItem{ - Name: entry.Name() + "/" + child.Name(), - Path: filepath.Join(root, entry.Name(), child.Name()), - }) - } - } - } - return items, nil -} - -func normalizeModelDownloaderRepoID(repoID string) (string, error) { - repoID = strings.TrimSpace(repoID) - if !re.GetRegex(re.ModelDownloaderRepoIDPattern).MatchString(repoID) { - return "", newModelDownloaderInputError("invalid repository ID: %s", repoID) - } - if strings.Contains(repoID, "..") { - return "", newModelDownloaderInputError("invalid repository ID: %s", repoID) - } - return repoID, nil -} - -func resolveDownloaderModelDir(dir string) string { - if strings.TrimSpace(dir) == "" { - return defaultModelDownloaderDir - } - return filepath.Clean(strings.TrimSpace(dir)) -} - -func ensureTargetDirAvailable(targetDir string) error { - if _, err := os.Stat(targetDir); err == nil { - return newModelDownloaderInputError("model directory already exists: %s", targetDir) - } else if !errors.Is(err, os.ErrNotExist) { - return err - } - return nil -} - -func filterModelDownloadFiles(items []dto.ModelFile) []dto.ModelFile { - hasSafeTensors := false - for _, item := range items { - if strings.HasSuffix(strings.ToLower(item.Name), ".safetensors") { - hasSafeTensors = true - break - } - } - filtered := make([]dto.ModelFile, 0, len(items)) - for _, item := range items { - name := strings.ToLower(filepath.ToSlash(item.Name)) - if hasSafeTensors { - if strings.HasSuffix(name, ".bin") || strings.HasPrefix(name, "original/") { - continue - } - if strings.HasPrefix(filepath.Base(name), "consolidated.") && strings.HasSuffix(name, ".pth") { - continue - } - } - filtered = append(filtered, item) - } - return filtered -} - -func mapHFModel(item hfModelResp) dto.ModelItem { - repoID := item.ID - if repoID == "" { - repoID = item.ModelID - } - size := calcSafetensorsSize(item.Safetensors) - return dto.ModelItem{ - RepoID: repoID, - Name: modelNameFromRepo(repoID), - Downloads: item.Downloads, - Likes: item.Likes, - Size: size, - SizeFormatted: formatBytes(size), - } -} - -func mapModelScopeModel(item modelScopeModelResp) dto.ModelItem { - repoID := item.ID - if repoID == "" { - repoID = item.ModelID - } - return dto.ModelItem{ - RepoID: repoID, - Name: modelNameFromRepo(repoID), - Downloads: item.Downloads, - Likes: item.Likes, - Size: item.FileSize, - SizeFormatted: formatBytes(item.FileSize), - } -} - -func calcSafetensorsSize(safetensors hfSafetensors) int64 { - dtypeBytes := map[string]int64{ - "F64": 8, "F32": 4, "F16": 2, "BF16": 2, - "I64": 8, "I32": 4, "I16": 2, "I8": 1, - "U64": 8, "U32": 4, "U16": 2, "U8": 1, - "BOOL": 1, - } - total := int64(0) - for dtype, num := range safetensors.Parameters { - width := dtypeBytes[strings.ToUpper(dtype)] - if width == 0 { - width = 1 - } - total += int64(num) * width - } - return total -} - -func formatBytes(size int64) string { - if size <= 0 { - return "" - } - const unit = 1024 - if size < unit*unit { - return fmt.Sprintf("%.1f KB", float64(size)/unit) - } - if size < unit*unit*unit { - return fmt.Sprintf("%.1f MB", float64(size)/(unit*unit)) - } - return fmt.Sprintf("%.1f GB", float64(size)/(unit*unit*unit)) -} - -func mapDownloadTask(item model.ModelDownloadTask) *dto.ModelDownloadTaskItem { - status := item.Status - return &dto.ModelDownloadTaskItem{ - DownloadType: item.DownloadType, - ID: item.ID, - Source: item.Source, - RepoID: item.RepoID, - Revision: item.Revision, - ModelName: item.ModelName, - TargetDir: item.TargetDir, - Status: status, - Progress: item.Progress, - TotalSize: item.TotalSize, - DownloadedSize: item.DownloadedSize, - ErrorMessage: item.ErrorMessage, - CreatedAt: item.CreatedAt.Format("2006-01-02 15:04:05"), - StartedAt: formatOptionalTime(item.StartedAt), - CompletedAt: formatOptionalTime(item.CompletedAt), - CanCancel: status == constant.StatusWaiting || status == statusDownloading, - CanRetry: status == constant.StatusFailed || status == constant.StatusCanceled, - CanRemoveRecord: status == constant.StatusSuccess || status == constant.StatusFailed || status == constant.StatusCanceled, - } -} - -func formatOptionalTime(value *time.Time) string { - if value == nil { - return "" - } - return value.Format("2006-01-02 15:04:05") -} - -func pageLocalModels(items []dto.LocalModelItem, page, pageSize int) []dto.LocalModelItem { - if page <= 0 { - page = 1 - } - if pageSize <= 0 { - pageSize = 20 - } - start := (page - 1) * pageSize - if start >= len(items) { - return []dto.LocalModelItem{} - } - end := start + pageSize - if end > len(items) { - end = len(items) - } - return items[start:end] -} - -func modelNameFromRepo(repoID string) string { - parts := strings.Split(repoID, "/") - if len(parts) == 0 { - return repoID - } - return parts[len(parts)-1] -} - -func hfEndpoint() string { - endpoint := loadModelDownloaderHFEndpointSetting() - if endpoint == "" { - endpoint = strings.TrimRight(os.Getenv("HF_ENDPOINT"), "/") - } - if endpoint == "" { - endpoint = defaultHFEndpoint - } - return endpoint -} - -func modelScopeEndpoint() string { - endpoint := loadModelDownloaderModelScopeEndpointSetting() - if endpoint == "" { - endpoint = defaultModelScopeEndpoint - } - return endpoint -} - -func normalizeModelSearch(req *dto.ModelSearch) { - if req.Page == 0 { - req.Page = 1 - } - if req.PageSize == 0 { - if req.Limit > 0 { - req.PageSize = req.Limit - } else { - req.PageSize = 50 - } - } - req.Limit = req.PageSize - if req.Sort == "" { - req.Sort = "trending" - } -} - -func buildHFModelSearchURL(endpoint string, req dto.ModelSearch) string { - query := url.Values{} - if keyword := strings.TrimSpace(req.Query); keyword != "" { - query.Set("search", keyword) - } - query.Set("sort", mapHFSort(req.Sort)) - query.Set("direction", "-1") - if req.Sort == "least_params" { - query.Set("direction", "1") - } - query.Set("limit", fmt.Sprintf("%d", req.PageSize)) - query.Set("skip", fmt.Sprintf("%d", (req.Page-1)*req.PageSize)) - query.Set("full", "true") - return endpoint + "/api/models?" + query.Encode() -} - -func estimateHFSearchTotal(req dto.ModelSearch, itemCount int) int64 { - offset := int64((req.Page - 1) * req.PageSize) - total := offset + int64(itemCount) - if itemCount == req.PageSize { - total++ - } - return total -} - -func mapHFSort(sort string) string { - switch sort { - case "downloads": - return "downloads" - case "likes": - return "likes" - case "updated": - return "lastModified" - case "created": - return "createdAt" - case "most_params", "least_params": - return "num_parameters" - default: - return "trendingScore" - } -} - -func mapModelScopeSort(sort string) string { - switch sort { - case "downloads": - return "downloads" - case "likes": - return "likes" - case "updated": - return "last_modified" - default: - return "default" - } -} - -func resolveRevision(source, revision string) string { - if strings.TrimSpace(revision) == "" { - if source == modelDownloaderSourceModelScope { - return "master" - } - return "main" - } - return strings.TrimSpace(revision) -} - -func escapedRepoPath(repoID string) string { - parts := strings.Split(repoID, "/") - for i := range parts { - parts[i] = url.PathEscape(parts[i]) - } - return strings.Join(parts, "/") -} - -func hfResolveURL(repoID, revision, filename string) string { - return hfEndpoint() + "/" + escapedRepoPath(repoID) + "/resolve/" + url.PathEscape(resolveRevision(modelDownloaderSourceHuggingFace, revision)) + "/" + pathEscapeFile(filename) -} - -func hfModelInfoURL(repoID, revision string) string { - query := url.Values{} - query.Set("blobs", "true") - query.Set("revision", resolveRevision(modelDownloaderSourceHuggingFace, revision)) - return hfEndpoint() + "/api/models/" + escapedRepoPath(repoID) + "?" + query.Encode() -} - -func modelFileResolveURL(source, repoID, revision, filename string) (string, error) { - switch source { - case modelDownloaderSourceHuggingFace: - return hfResolveURL(repoID, revision, filename), nil - case modelDownloaderSourceModelScope: - return buildModelScopeResolveURL(modelScopeEndpoint(), repoID, revision, filename), nil - default: - return "", newModelDownloaderInputError("unsupported model source: %s", source) - } -} - -func buildModelScopeResolveURL(endpoint, repoID, revision, filename string) string { - query := url.Values{} - query.Set("Revision", resolveRevision(modelDownloaderSourceModelScope, revision)) - query.Set("FilePath", filename) - return strings.TrimRight(endpoint, "/") + "/api/v1/models/" + escapedRepoPath(repoID) + "/repo?" + query.Encode() -} - -func applyDownloadAuth(req *http.Request, source string) { - if source == modelDownloaderSourceModelScope { - applyModelScopeAPIToken(req, loadModelDownloaderModelScopeToken()) - return - } - if token := loadModelDownloaderHFToken(); token != "" { - req.Header.Set("Authorization", "Bearer "+token) - } -} - -func applyModelScopeAPIToken(req *http.Request, token string) { - if token != "" { - req.AddCookie(&http.Cookie{Name: "m_session_id", Value: token}) - } -} - -func pathEscapeFile(filename string) string { - parts := strings.Split(filepath.ToSlash(filename), "/") - for i := range parts { - parts[i] = url.PathEscape(parts[i]) - } - return strings.Join(parts, "/") -} - -func safeJoinModelFile(baseDir, filename string) (string, error) { - if strings.TrimSpace(filename) == "" { - return "", fmt.Errorf("invalid model file path") - } - normalized := filepath.FromSlash(strings.ReplaceAll(filename, "\\", "/")) - if filepath.IsAbs(normalized) { - return "", fmt.Errorf("invalid model file path: %s", filename) - } - cleanName := filepath.Clean(normalized) - if cleanName == "." || cleanName == ".." || strings.HasPrefix(cleanName, ".."+string(filepath.Separator)) { - return "", fmt.Errorf("invalid model file path: %s", filename) - } - base := filepath.Clean(baseDir) - target := filepath.Join(base, cleanName) - rel, err := filepath.Rel(base, target) - if err != nil { - return "", err - } - if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { - return "", fmt.Errorf("invalid model file path: %s", filename) - } - return target, nil -} - -func taskTempDir(task model.ModelDownloadTask) string { - return filepath.Join(filepath.Dir(task.TargetDir), fmt.Sprintf(".%s-%d.download", task.ModelName, task.ID)) -} - -func loadModelDownloaderDir() string { - value, err := settingRepo.GetValueByKey(modelDownloaderDirKey) - if err != nil { - return defaultModelDownloaderDir - } - return resolveDownloaderModelDir(value) -} - -func loadModelDownloaderHFEndpointSetting() string { - return loadModelDownloaderEndpointSetting(modelDownloaderEndpointKey) -} - -func loadModelDownloaderHFToken() string { - return loadModelDownloaderSetting(modelDownloaderTokenKey) -} - -func loadModelDownloaderModelScopeEndpointSetting() string { - return loadModelDownloaderEndpointSetting(modelDownloaderMSEndpointKey) -} - -func loadModelDownloaderModelScopeToken() string { - return loadModelDownloaderSetting(modelDownloaderMSTokenKey) -} - -func updateModelDownloaderEndpointSetting(key, value string) error { - return updateModelDownloaderSetting(key, cleanModelDownloaderEndpoint(value)) -} - -func updateModelDownloaderSetting(key, value string) error { - return settingRepo.UpdateOrCreate(key, strings.TrimSpace(value)) -} - -func loadModelDownloaderEndpointSetting(key string) string { - return cleanModelDownloaderEndpoint(loadModelDownloaderSetting(key)) -} - -func loadModelDownloaderSetting(key string) string { - value, err := settingRepo.GetValueByKey(key) - if err != nil { - return "" - } - return strings.TrimSpace(value) -} - -func cleanModelDownloaderEndpoint(value string) string { - return strings.TrimRight(strings.TrimSpace(value), "/") -} diff --git a/agent/app/service/model_downloader_options.go b/agent/app/service/model_downloader_options.go deleted file mode 100644 index d8ad1ebe7..000000000 --- a/agent/app/service/model_downloader_options.go +++ /dev/null @@ -1,63 +0,0 @@ -package service - -import ( - "os" - "path/filepath" - "sort" - "strings" - - "github.com/1Panel-dev/1Panel/agent/app/dto" -) - -func listLocalModelOptions(root string) ([]dto.LocalModelOption, error) { - items := make([]dto.LocalModelOption, 0) - entries, err := os.ReadDir(root) - if os.IsNotExist(err) { - return items, nil - } - if err != nil { - return nil, err - } - var scan func(string, string) error - scan = func(path, organization string) error { - children, err := os.ReadDir(path) - if err != nil { - return err - } - for _, child := range children { - if child.IsDir() || child.Type()&os.ModeSymlink != 0 || strings.HasPrefix(child.Name(), ".") { - continue - } - name := child.Name() - if name == "config.json" || name == "params.json" { - items = append(items, dto.LocalModelOption{Name: filepath.Base(path), Organization: organization, Path: path}) - return nil - } - } - if organization == "" { - for _, child := range children { - if child.IsDir() && !strings.HasPrefix(child.Name(), ".") { - if err := scan(filepath.Join(path, child.Name()), filepath.Base(path)); err != nil { - return err - } - } - } - } - return nil - } - for _, entry := range entries { - if entry.IsDir() && !strings.HasPrefix(entry.Name(), ".") { - if err := scan(filepath.Join(root, entry.Name()), ""); err != nil { - return nil, err - } - } - } - sort.Slice(items, func(i, j int) bool { - left, right := strings.ToLower(items[i].Name), strings.ToLower(items[j].Name) - if left == right { - return items[i].Path < items[j].Path - } - return left < right - }) - return items, nil -} diff --git a/agent/app/service/model_downloader_tokenizer.go b/agent/app/service/model_downloader_tokenizer.go deleted file mode 100644 index bc2d112d1..000000000 --- a/agent/app/service/model_downloader_tokenizer.go +++ /dev/null @@ -1,149 +0,0 @@ -package service - -import ( - "encoding/json" - "io/fs" - "os" - "path/filepath" - "strings" - - "github.com/1Panel-dev/1Panel/agent/app/dto" - "github.com/1Panel-dev/1Panel/agent/global" - "github.com/1Panel-dev/1Panel/agent/utils/files" -) - -const tokenizerDownloadMaxSize = 256 << 20 - -type tokenizerDownloadMetadata struct { - Source string `json:"source"` - RepoID string `json:"repoID"` - Revision string `json:"revision"` -} - -func filterTokenizerDownloadFiles(items []dto.ModelFile) []dto.ModelFile { - result := make([]dto.ModelFile, 0) - for _, item := range items { - name := item.Name - if !filepath.IsLocal(name) || strings.ContainsAny(name, "\\\x00\r\n") || filepath.ToSlash(filepath.Clean(name)) != name { - continue - } - if strings.Contains(name, "/") { - if strings.HasPrefix(name, "chat_templates/") && strings.HasSuffix(name, ".jinja") { - result = append(result, item) - } - continue - } - switch name { - case "config.json", "tokenizer_config.json", "tokenizer.json", "special_tokens_map.json", "added_tokens.json", "vocab.json", "vocab.txt", "merges.txt", "tekken.json": - result = append(result, item) - default: - if strings.HasSuffix(name, ".model") || strings.HasPrefix(name, "tokenizer.model") || strings.HasSuffix(name, ".tiktoken") || strings.HasSuffix(name, ".py") || strings.HasSuffix(name, ".jinja") { - result = append(result, item) - } - } - } - return result -} - -func validateTokenizerDownloadFiles(items []dto.ModelFile) error { - if len(items) == 0 || len(items) > 512 { - return newModelDownloaderInputError("tokenizer must contain between 1 and 512 files") - } - names := make(map[string]bool, len(items)) - var size int64 - vocabulary := false - for _, item := range items { - if names[item.Name] || item.Size < 0 || item.Size > tokenizerDownloadMaxSize-size { - return newModelDownloaderInputError("invalid tokenizer files or total size exceeds 256 MiB") - } - size += item.Size - names[item.Name] = true - if item.Name == "tokenizer.json" || item.Name == "tekken.json" || item.Name == "vocab.txt" || strings.HasSuffix(item.Name, ".model") || strings.HasPrefix(item.Name, "tokenizer.model") || strings.HasSuffix(item.Name, ".tiktoken") { - vocabulary = true - } - } - if !vocabulary && !(names["vocab.json"] && names["merges.txt"]) { - return newModelDownloaderInputError("repository has no supported tokenizer vocabulary in its root directory") - } - return nil -} - -func validateDownloadedTokenizer(root string) error { - var items []dto.ModelFile - err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error { - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - stat, err := entry.Info() - if err != nil { - return err - } - if !stat.Mode().IsRegular() || stat.Size() > tokenizerDownloadMaxSize { - return newModelDownloaderInputError("invalid tokenizer file") - } - name, err := filepath.Rel(root, path) - if err != nil { - return err - } - items = append(items, dto.ModelFile{Name: filepath.ToSlash(name), Size: stat.Size()}) - if strings.HasSuffix(name, ".json") { - content, err := os.ReadFile(path) - if err != nil { - return err - } - if !json.Valid(content) { - return newModelDownloaderInputError("invalid tokenizer JSON: %s", name) - } - } - return nil - }) - if err != nil { - return err - } - return validateTokenizerDownloadFiles(items) -} - -func listDownloadedTokenizers(info string) ([]dto.LocalModelItem, error) { - items := make([]dto.LocalModelItem, 0) - root := filepath.Join(global.CONF.Base.InstallDir, "1panel/ai/tokenizer") - directories, err := listModelDownloadDirectories(root) - if err != nil { - return nil, err - } - for _, item := range directories { - if _, err := normalizeModelDownloaderRepoID(item.Name); err != nil { - continue - } - path, err := filepath.EvalSymlinks(item.Path) - if err != nil { - return nil, err - } - content, err := os.ReadFile(filepath.Join(path, ".1panel-tokenizer")) - if os.IsNotExist(err) { - continue - } - if err != nil { - return nil, err - } - var metadata tokenizerDownloadMetadata - if err := json.Unmarshal(content, &metadata); err != nil { - return nil, err - } - if info != "" && !strings.Contains(strings.ToLower(metadata.RepoID), strings.ToLower(info)) { - continue - } - stat, err := os.Stat(path) - if err != nil { - return nil, err - } - size, err := files.NewFileOp().GetDirSize(path) - if err != nil { - return nil, err - } - items = append(items, dto.LocalModelItem{Name: item.Name, Path: path, RepoID: metadata.RepoID, Source: metadata.Source, Revision: metadata.Revision, DownloadType: "tokenizer", Size: size, SizeFormatted: formatBytes(size), CreatedAt: stat.ModTime().Format("2006-01-02 15:04:05")}) - } - return items, nil -} diff --git a/agent/cmd/server/docs/x-log.json b/agent/cmd/server/docs/x-log.json index d21b48e7a..09fdbf89e 100644 --- a/agent/cmd/server/docs/x-log.json +++ b/agent/cmd/server/docs/x-log.json @@ -8,7 +8,7 @@ "formatZH": "更新 Ollama 绑定域名 [domain]", "formatEN": "update Ollama bind domain [domain]" }, - "/ai/model/downloader/featured/sync/status": { + "/xpack/model/downloader/featured/sync/status": { "bodyKeys": [ "value" ], @@ -17,7 +17,7 @@ "formatZH": "更新推荐模型自动同步 [value]", "formatEN": "update featured model automatic synchronization [value]" }, - "/ai/model/downloader/hf/download": { + "/xpack/model/downloader/hf/download": { "bodyKeys": [ "repoID", "revision" @@ -27,7 +27,7 @@ "formatZH": "下载 Hugging Face 模型 [repoID]", "formatEN": "download Hugging Face model [repoID]" }, - "/ai/model/downloader/local/delete": { + "/xpack/model/downloader/local/delete": { "bodyKeys": [ "name" ], @@ -36,7 +36,7 @@ "formatZH": "删除本地模型 [name]", "formatEN": "delete local model [name]" }, - "/ai/model/downloader/modelscope/download": { + "/xpack/model/downloader/modelscope/download": { "bodyKeys": [ "repoID", "revision" @@ -46,7 +46,7 @@ "formatZH": "下载 ModelScope 模型 [repoID]", "formatEN": "download ModelScope model [repoID]" }, - "/ai/model/downloader/settings": { + "/xpack/model/downloader/settings": { "bodyKeys": [ "modelDir" ], @@ -55,7 +55,7 @@ "formatZH": "更新模型下载设置 [modelDir]", "formatEN": "update model downloader settings [modelDir]" }, - "/ai/model/downloader/tasks/cancel": { + "/xpack/model/downloader/tasks/cancel": { "bodyKeys": [ "id" ], @@ -73,7 +73,7 @@ "formatZH": "取消模型下载任务 [repoID]", "formatEN": "cancel model download task [repoID]" }, - "/ai/model/downloader/tasks/remove": { + "/xpack/model/downloader/tasks/remove": { "bodyKeys": [ "id" ], @@ -91,7 +91,7 @@ "formatZH": "移除模型下载任务 [repoID]", "formatEN": "remove model download task [repoID]" }, - "/ai/model/downloader/tasks/retry": { + "/xpack/model/downloader/tasks/retry": { "bodyKeys": [ "id" ], diff --git a/agent/cron/cron.go b/agent/cron/cron.go index 3a6cb4da4..5b554c2b5 100644 --- a/agent/cron/cron.go +++ b/agent/cron/cron.go @@ -26,12 +26,6 @@ func Run() { interval model.Setting status model.Setting ) - if !global.CONF.Base.IsOffline { - if _, err := global.Cron.AddFunc("@every 24h", service.SyncFeaturedModels); err != nil { - global.LOG.Errorf("add featured model sync job failed: %v", err) - } - go service.SyncFeaturedModels() - } go syncBeforeStart() if err := global.DB.Where("key = ?", "MonitorStatus").Find(&status).Error; err != nil { global.LOG.Errorf("load monitor status from db failed, err: %v", err) diff --git a/agent/init/business/business.go b/agent/init/business/business.go index 12c5611c7..dece5c156 100644 --- a/agent/init/business/business.go +++ b/agent/init/business/business.go @@ -10,12 +10,6 @@ import ( ) func Init() { - global.LOG.Info("recover interrupted model download tasks start") - if err := service.NewIModelDownloaderService().RecoverInterruptedTasks(); err != nil { - global.LOG.Warnf("recover interrupted model download tasks failed, err: %v", err) - } - global.LOG.Info("recover interrupted model download tasks done") - go syncApp() go syncInstalledApp() go syncRuntime() diff --git a/agent/init/hook/hook.go b/agent/init/hook/hook.go index f9a907e75..3e6287dd7 100644 --- a/agent/init/hook/hook.go +++ b/agent/init/hook/hook.go @@ -18,38 +18,18 @@ import ( ) func Init() { - global.LOG.Info("agent hook: init global data start") initGlobalData() - global.LOG.Info("agent hook: init global data done") - global.LOG.Info("agent hook: handle cronjob status start") handleCronjobStatus() - global.LOG.Info("agent hook: handle cronjob status done") - global.LOG.Info("agent hook: handle clam status start") handleClamStatus() - global.LOG.Info("agent hook: handle clam status done") - global.LOG.Info("agent hook: handle record status start") handleRecordStatus() - global.LOG.Info("agent hook: handle record status done") - global.LOG.Info("agent hook: handle snapshot status start") handleSnapStatus() - global.LOG.Info("agent hook: handle snapshot status done") - global.LOG.Info("agent hook: handle ollama model status start") handleOllamaModelStatus() - global.LOG.Info("agent hook: handle ollama model status done") - global.LOG.Info("agent hook: load local dir start") loadLocalDir() - global.LOG.Info("agent hook: load local dir done") - global.LOG.Info("agent hook: init docker config start") initDockerConf() - global.LOG.Info("agent hook: init docker config done") - global.LOG.Info("agent hook: init alert task start") initAlertTask() - global.LOG.Info("agent hook: init alert task done") - global.LOG.Info("agent hook: init monitor db start") initMonitorDB() - global.LOG.Info("agent hook: init monitor db done") } func initGlobalData() { diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go index eaf47b399..9437be7b8 100644 --- a/agent/init/migration/migrate.go +++ b/agent/init/migration/migrate.go @@ -119,9 +119,7 @@ func agentDBMigrations() []*gormigrate.Migration { migrations.AddOllamaModelAccountSync, migrations.AddContainerImageCheckUpdates, migrations.AddModelQuickJump, - migrations.MigrateModelDownloader, migrations.MigrateVMDatabase, - migrations.AddFeaturedModelWeightProvider, } } diff --git a/agent/init/migration/migrations/model_downloader.go b/agent/init/migration/migrations/model_downloader.go deleted file mode 100644 index 220d07ad1..000000000 --- a/agent/init/migration/migrations/model_downloader.go +++ /dev/null @@ -1,140 +0,0 @@ -package migrations - -import ( - "errors" - "net/url" - "os" - "path/filepath" - - "github.com/1Panel-dev/1Panel/agent/app/model" - "github.com/1Panel-dev/1Panel/agent/global" - "github.com/glebarez/sqlite" - "github.com/go-gormigrate/gormigrate/v2" - "gorm.io/gorm" - "gorm.io/gorm/clause" -) - -var MigrateModelDownloader = &gormigrate.Migration{ - ID: "20261102-migrate-model-downloader", - Migrate: func(db *gorm.DB) error { - return migrateModelDownloader(db, filepath.Join(global.Dir.DbDir, "xpack.db")) - }, -} - -var AddFeaturedModelWeightProvider = &gormigrate.Migration{ - ID: "20261103-add-featured-model-weight-provider", - Migrate: func(tx *gorm.DB) error { - if err := tx.AutoMigrate(&model.FeaturedModel{}); err != nil { - return err - } - if err := tx.Model(&model.FeaturedModel{}). - Where("(weight_provider IS NULL OR weight_provider = '') AND instr(repo_id, '/') > 0"). - Update("weight_provider", gorm.Expr("substr(repo_id, 1, instr(repo_id, '/') - 1)")).Error; err != nil { - return err - } - return tx.Model(&model.Setting{}).Where("key = ?", "FeaturedModelsVersion").Update("value", "").Error - }, -} - -func migrateModelDownloader(db *gorm.DB, legacyPath string) error { - var legacy *gorm.DB - if _, err := os.Stat(legacyPath); err == nil { - source := url.URL{Scheme: "file", Path: legacyPath, RawQuery: "mode=ro"} - legacy, err = gorm.Open(sqlite.Open(source.String()), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}) - if err != nil { - return err - } - sqlDB, err := legacy.DB() - if err != nil { - return err - } - defer sqlDB.Close() - } else if !errors.Is(err, os.ErrNotExist) { - return err - } - return db.Transaction(func(tx *gorm.DB) error { - if err := tx.AutoMigrate(&model.ModelDownloadTask{}, &model.FeaturedModel{}); err != nil { - return err - } - resetVersion := false - if legacy != nil { - if legacy.Migrator().HasTable(&model.ModelDownloadTask{}) { - var tasks []model.ModelDownloadTask - if err := legacy.FindInBatches(&tasks, 100, func(_ *gorm.DB, _ int) error { - for i := range tasks { - if tasks[i].DownloadType == "" { - tasks[i].DownloadType = "all" - } - } - return tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&tasks).Error - }).Error; err != nil { - return err - } - } - if legacy.Migrator().HasTable(&model.FeaturedModel{}) { - resetVersion = !legacy.Migrator().HasColumn(&model.FeaturedModel{}, "Sources") || !legacy.Migrator().HasColumn(&model.FeaturedModel{}, "SourceModelIDs") - var models []model.FeaturedModel - if err := legacy.FindInBatches(&models, 100, func(_ *gorm.DB, _ int) error { - for i := range models { - if len(models[i].Sources) == 0 { - models[i].Sources = []string{"modelscope"} - } - } - return tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&models).Error - }).Error; err != nil { - return err - } - } else { - resetVersion = true - } - if legacy.Migrator().HasTable("agent_settings") { - var settings []model.Setting - keys := []string{"ModelDownloaderDir", "ModelDownloaderHFEndpoint", "ModelDownloaderHFToken", "ModelDownloaderModelScopeEndpoint", "ModelDownloaderModelScopeToken", "FeaturedModelsSync", "FeaturedModelsVersion"} - if err := legacy.Table("agent_settings").Where("key IN ?", keys).Find(&settings).Error; err != nil { - return err - } - for _, setting := range settings { - if setting.Key == "FeaturedModelsVersion" && resetVersion { - setting.Value = "" - } - if err := tx.Where("key = ?", setting.Key).FirstOrCreate(&model.Setting{Key: setting.Key, Value: setting.Value, About: setting.About}).Error; err != nil { - return err - } - } - } - } - var count int64 - if err := tx.Model(&model.FeaturedModel{}).Count(&count).Error; err != nil { - return err - } - if count > 0 { - return nil - } - if err := tx.Model(&model.Setting{}).Where("key = ?", "FeaturedModelsVersion").Update("value", "").Error; err != nil { - return err - } - models := []model.FeaturedModel{ - {Family: "Qwen", RepoID: "Qwen/Qwen3.6-35B-A3B", Architecture: "MoE", BackboneParams: "35B", ActiveParams: "3B", Engram: "N/A", WeightGB: 67}, - {Family: "Qwen", RepoID: "Qwen/Qwen3.6-35B-A3B-FP8", Architecture: "MoE", BackboneParams: "35B", ActiveParams: "3B", Engram: "N/A", WeightGB: 35}, - {Family: "Qwen", RepoID: "nvidia/Qwen3.6-35B-A3B-NVFP4", Architecture: "MoE", BackboneParams: "35B", ActiveParams: "3B", Engram: "N/A", WeightGB: 22}, - {Family: "Qwen", RepoID: "Qwen/Qwen3.8-27B", Architecture: "Dense", BackboneParams: "N/A", ActiveParams: "N/A", Engram: "N/A", WeightGB: 55}, - {Family: "Qwen", RepoID: "Qwen/Qwen3.8-27B-FP8", Architecture: "Dense", BackboneParams: "N/A", ActiveParams: "N/A", Engram: "N/A", WeightGB: 28}, - {Family: "Qwen", RepoID: "nvidia/Qwen3.8-27B-NVFP4", Architecture: "Dense", BackboneParams: "N/A", ActiveParams: "N/A", Engram: "N/A", WeightGB: 22}, - {Family: "Qwen", RepoID: "Qwen/Qwen3.8-Flash-Next", Architecture: "MoE", BackboneParams: "125B", ActiveParams: "6B", Engram: "51B", WeightGB: 335}, - {Family: "Qwen", RepoID: "Qwen/Qwen3.8-Flash-Next-FP8", Architecture: "MoE", BackboneParams: "125B", ActiveParams: "6B", Engram: "51B", WeightGB: 172}, - {Family: "Qwen", RepoID: "nvidia/Qwen3.8-Flash-Next-FP4-Local", Architecture: "MoE", BackboneParams: "125B", ActiveParams: "6B", Engram: "51B", WeightGB: 130}, - {Family: "DeepSeek", RepoID: "deepseek-ai/DeepSeek-V4-Flash-0731", Architecture: "MoE", BackboneParams: "284B", ActiveParams: "13B", Engram: "N/A", WeightGB: 167}, - {Family: "DeepSeek", RepoID: "deepseek-ai/DeepSeek-V4-Flash-Vision-Exp", Architecture: "MoE", BackboneParams: "305B", ActiveParams: "undisclosed", Engram: "N/A", WeightGB: 167}, - {Family: "DeepSeek", RepoID: "deepseek-ai/DeepSeek-V4.1-Flash-1017", Architecture: "MoE", BackboneParams: "552B", ActiveParams: "8B/16B", Engram: "196B", WeightGB: 510}, - {Family: "GLM", RepoID: "zai-org/GLM-5.3-Flash", Architecture: "MoE", BackboneParams: "320B", ActiveParams: "18B", Engram: "N/A", WeightGB: 306}, - {Family: "GLM", RepoID: "nvidia/GLM-5.3-Flash-FP4-Local", Architecture: "MoE", BackboneParams: "320B", ActiveParams: "18B", Engram: "N/A", WeightGB: 185}, - } - for i := range models { - models[i].Key = models[i].RepoID - models[i].Sort = i + 1 - models[i].Description = "{}" - models[i].Sources = []string{"modelscope"} - } - return tx.Create(&models).Error - }) -} diff --git a/agent/middleware/operation.go b/agent/middleware/operation.go index 6ff385a0e..6bae99dc4 100644 --- a/agent/middleware/operation.go +++ b/agent/middleware/operation.go @@ -60,9 +60,6 @@ func OperationResolveMeta() gin.HandlerFunc { return } reqPath := strings.TrimPrefix(c.Request.URL.Path, "/api/v2") - if strings.HasPrefix(reqPath, "/xpack/model/downloader/") { - reqPath = strings.Replace(reqPath, "/xpack/model/downloader/", "/ai/model/downloader/", 1) - } meta, ok := metaMap[reqPath] if !ok || len(meta.BeforeFunctions) == 0 { c.Next() diff --git a/agent/router/common.go b/agent/router/common.go index b1f66f609..f0c048def 100644 --- a/agent/router/common.go +++ b/agent/router/common.go @@ -24,7 +24,6 @@ func commonGroups() []CommonRouter { &ProcessRouter{}, &WebsiteCARouter{}, &AIToolsRouter{}, - &ModelDownloaderRouter{}, &GroupRouter{}, &AlertRouter{}, &RuntimeDiagnosticsRouter{}, diff --git a/agent/router/ro_model_downloader.go b/agent/router/ro_model_downloader.go deleted file mode 100644 index 0942ad1a3..000000000 --- a/agent/router/ro_model_downloader.go +++ /dev/null @@ -1,33 +0,0 @@ -package router - -import ( - v2 "github.com/1Panel-dev/1Panel/agent/app/api/v2" - "github.com/gin-gonic/gin" -) - -type ModelDownloaderRouter struct{} - -func (m *ModelDownloaderRouter) InitRouter(Router *gin.RouterGroup) { - baseApi := v2.ApiGroupApp.BaseApi - for _, prefix := range []string{"ai/model/downloader", "xpack/model/downloader"} { - router := Router.Group(prefix) - router.POST("/featured/search", baseApi.PageFeaturedModels) - router.POST("/featured/sync", baseApi.SyncFeaturedModels) - router.POST("/featured/sync/status", baseApi.UpdateFeaturedModelsSync) - router.POST("/hf/search", baseApi.SearchHFModels) - router.POST("/hf/info", baseApi.GetHFModelInfo) - router.POST("/hf/download", baseApi.StartHFModelDownload) - router.POST("/modelscope/search", baseApi.SearchModelScopeModels) - router.POST("/modelscope/info", baseApi.GetModelScopeModelInfo) - router.POST("/modelscope/download", baseApi.StartModelScopeModelDownload) - router.GET("/settings", baseApi.GetModelDownloaderSettings) - router.POST("/settings", baseApi.UpdateModelDownloaderSettings) - router.POST("/tasks/search", baseApi.PageModelDownloadTasks) - router.POST("/tasks/cancel", baseApi.CancelModelDownloadTask) - router.POST("/tasks/retry", baseApi.RetryModelDownloadTask) - router.POST("/tasks/remove", baseApi.RemoveModelDownloadTask) - router.POST("/local/search", baseApi.PageLocalDownloadedModels) - router.GET("/local/options", baseApi.ListLocalModelOptions) - router.POST("/local/delete", baseApi.DeleteLocalDownloadedModel) - } -} diff --git a/agent/server/server.go b/agent/server/server.go index 5af79494e..f71a49a51 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -97,36 +97,23 @@ func Start() { viper.Init() dir.Init() log.Init() - global.LOG.Info("agent startup: logger initialized") if err := files.CleanupInterruptedDownloads(); err != nil { global.LOG.Warnf("clean interrupted remote downloads: %s", err) } db.Init() - global.LOG.Info("agent startup: database initialized") migration.Init() - global.LOG.Info("agent startup: migration initialized") i18n.Init() - global.LOG.Info("agent startup: i18n initialized") cache.Init() - global.LOG.Info("agent startup: cache initialized") app.Init() - global.LOG.Info("agent startup: app initialized") lang.Init() - global.LOG.Info("agent startup: language initialized") validator.Init() - global.LOG.Info("agent startup: validator initialized") cron.Run() - global.LOG.Info("agent startup: cron initialized") hook.Init() - global.LOG.Info("agent startup: hook initialized") go firewall.Init() - global.LOG.Info("agent startup: firewall init scheduled") InitOthers() - global.LOG.Info("agent startup: edition initialized") kvm.Init() rootRouter := router.Routers() - global.LOG.Info("agent startup: router initialized") server := &http.Server{ Handler: rootRouter, @@ -139,7 +126,6 @@ func Start() { } if global.IsMaster { - global.LOG.Infof("agent startup: master mode, preparing unix socket %s", masterSocketPath) if err := prepareMasterSocketDir(masterSocketDir); err != nil { panic(err) } @@ -152,14 +138,12 @@ func Start() { _ = listener.Close() panic(err) } - global.LOG.Infof("agent startup: listening on unix socket %s", masterSocketPath) business.Init() - global.LOG.Info("agent startup: business initialized") + global.LOG.Infof("agent startup: listening on unix socket %s", masterSocketPath) _ = server.Serve(listener) return } else { server.Addr = fmt.Sprintf("0.0.0.0:%s", global.CONF.Base.Port) - global.LOG.Infof("agent startup: node mode, preparing https listener %s", server.Addr) settingRepo := repo.NewISettingRepo() certItem, err := settingRepo.Get(settingRepo.WithByKey("ServerCrt")) if err != nil { @@ -189,7 +173,6 @@ func Start() { server.TLSConfig.ClientCAs = caCertPool } business.Init() - global.LOG.Info("agent startup: business initialized") global.LOG.Infof("listen at https://0.0.0.0:%s", global.CONF.Base.Port) if err := server.ListenAndServeTLS("", ""); err != nil { panic(err) diff --git a/core/cmd/server/docs/docs.go b/core/cmd/server/docs/docs.go index b2dda52f6..b85b539e4 100644 --- a/core/cmd/server/docs/docs.go +++ b/core/cmd/server/docs/docs.go @@ -3337,7 +3337,7 @@ const docTemplate = `{ ] } }, - "/ai/model/downloader/featured/search": { + "/xpack/model/downloader/featured/search": { "post": { "consumes": [ "application/json" @@ -3393,7 +3393,7 @@ const docTemplate = `{ ] } }, - "/ai/model/downloader/featured/sync": { + "/xpack/model/downloader/featured/sync": { "post": { "consumes": [ "application/json" @@ -3421,7 +3421,7 @@ const docTemplate = `{ ] } }, - "/ai/model/downloader/featured/sync/status": { + "/xpack/model/downloader/featured/sync/status": { "post": { "consumes": [ "application/json" @@ -3469,12 +3469,12 @@ const docTemplate = `{ } } }, - "/ai/model/downloader/hf/download": { + "/xpack/model/downloader/hf/download": { "post": { "consumes": [ "application/json" ], - "description": "Creates an asynchronous download task. Query /ai/model/downloader/tasks/search for progress.", + "description": "Creates an asynchronous download task. Query /xpack/model/downloader/tasks/search for progress.", "parameters": [ { "description": "request", @@ -3521,7 +3521,7 @@ const docTemplate = `{ } } }, - "/ai/model/downloader/hf/info": { + "/xpack/model/downloader/hf/info": { "post": { "consumes": [ "application/json" @@ -3562,7 +3562,7 @@ const docTemplate = `{ ] } }, - "/ai/model/downloader/hf/search": { + "/xpack/model/downloader/hf/search": { "post": { "consumes": [ "application/json" @@ -3618,7 +3618,7 @@ const docTemplate = `{ ] } }, - "/ai/model/downloader/local/delete": { + "/xpack/model/downloader/local/delete": { "post": { "consumes": [ "application/json" @@ -3665,7 +3665,7 @@ const docTemplate = `{ } } }, - "/ai/model/downloader/local/search": { + "/xpack/model/downloader/local/search": { "post": { "consumes": [ "application/json" @@ -3721,12 +3721,12 @@ const docTemplate = `{ ] } }, - "/ai/model/downloader/modelscope/download": { + "/xpack/model/downloader/modelscope/download": { "post": { "consumes": [ "application/json" ], - "description": "Creates an asynchronous download task. Query /ai/model/downloader/tasks/search for progress.", + "description": "Creates an asynchronous download task. Query /xpack/model/downloader/tasks/search for progress.", "parameters": [ { "description": "request", @@ -3773,7 +3773,7 @@ const docTemplate = `{ } } }, - "/ai/model/downloader/modelscope/info": { + "/xpack/model/downloader/modelscope/info": { "post": { "consumes": [ "application/json" @@ -3814,7 +3814,7 @@ const docTemplate = `{ ] } }, - "/ai/model/downloader/modelscope/search": { + "/xpack/model/downloader/modelscope/search": { "post": { "consumes": [ "application/json" @@ -3870,7 +3870,7 @@ const docTemplate = `{ ] } }, - "/ai/model/downloader/settings": { + "/xpack/model/downloader/settings": { "get": { "produces": [ "application/json" @@ -3900,7 +3900,7 @@ const docTemplate = `{ "consumes": [ "application/json" ], - "description": "Updates the download directory, source endpoints and tokens. The featuredModelsSync field is ignored; use /ai/model/downloader/featured/sync/status to update automatic synchronization.", + "description": "Updates the download directory, source endpoints and tokens. The featuredModelsSync field is ignored; use /xpack/model/downloader/featured/sync/status to update automatic synchronization.", "parameters": [ { "description": "request", @@ -3943,7 +3943,7 @@ const docTemplate = `{ } } }, - "/ai/model/downloader/tasks/cancel": { + "/xpack/model/downloader/tasks/cancel": { "post": { "consumes": [ "application/json" @@ -4000,7 +4000,7 @@ const docTemplate = `{ } } }, - "/ai/model/downloader/tasks/remove": { + "/xpack/model/downloader/tasks/remove": { "post": { "consumes": [ "application/json" @@ -4057,7 +4057,7 @@ const docTemplate = `{ } } }, - "/ai/model/downloader/tasks/retry": { + "/xpack/model/downloader/tasks/retry": { "post": { "consumes": [ "application/json" @@ -4117,7 +4117,7 @@ const docTemplate = `{ } } }, - "/ai/model/downloader/tasks/search": { + "/xpack/model/downloader/tasks/search": { "post": { "consumes": [ "application/json" diff --git a/core/cmd/server/docs/swagger.json b/core/cmd/server/docs/swagger.json index 3469dfd3e..b779e2827 100644 --- a/core/cmd/server/docs/swagger.json +++ b/core/cmd/server/docs/swagger.json @@ -3330,7 +3330,7 @@ ] } }, - "/ai/model/downloader/featured/search": { + "/xpack/model/downloader/featured/search": { "post": { "consumes": [ "application/json" @@ -3386,7 +3386,7 @@ ] } }, - "/ai/model/downloader/featured/sync": { + "/xpack/model/downloader/featured/sync": { "post": { "consumes": [ "application/json" @@ -3414,7 +3414,7 @@ ] } }, - "/ai/model/downloader/featured/sync/status": { + "/xpack/model/downloader/featured/sync/status": { "post": { "consumes": [ "application/json" @@ -3462,12 +3462,12 @@ } } }, - "/ai/model/downloader/hf/download": { + "/xpack/model/downloader/hf/download": { "post": { "consumes": [ "application/json" ], - "description": "Creates an asynchronous download task. Query /ai/model/downloader/tasks/search for progress.", + "description": "Creates an asynchronous download task. Query /xpack/model/downloader/tasks/search for progress.", "parameters": [ { "description": "request", @@ -3514,7 +3514,7 @@ } } }, - "/ai/model/downloader/hf/info": { + "/xpack/model/downloader/hf/info": { "post": { "consumes": [ "application/json" @@ -3555,7 +3555,7 @@ ] } }, - "/ai/model/downloader/hf/search": { + "/xpack/model/downloader/hf/search": { "post": { "consumes": [ "application/json" @@ -3611,7 +3611,7 @@ ] } }, - "/ai/model/downloader/local/delete": { + "/xpack/model/downloader/local/delete": { "post": { "consumes": [ "application/json" @@ -3658,7 +3658,7 @@ } } }, - "/ai/model/downloader/local/search": { + "/xpack/model/downloader/local/search": { "post": { "consumes": [ "application/json" @@ -3714,12 +3714,12 @@ ] } }, - "/ai/model/downloader/modelscope/download": { + "/xpack/model/downloader/modelscope/download": { "post": { "consumes": [ "application/json" ], - "description": "Creates an asynchronous download task. Query /ai/model/downloader/tasks/search for progress.", + "description": "Creates an asynchronous download task. Query /xpack/model/downloader/tasks/search for progress.", "parameters": [ { "description": "request", @@ -3766,7 +3766,7 @@ } } }, - "/ai/model/downloader/modelscope/info": { + "/xpack/model/downloader/modelscope/info": { "post": { "consumes": [ "application/json" @@ -3807,7 +3807,7 @@ ] } }, - "/ai/model/downloader/modelscope/search": { + "/xpack/model/downloader/modelscope/search": { "post": { "consumes": [ "application/json" @@ -3863,7 +3863,7 @@ ] } }, - "/ai/model/downloader/settings": { + "/xpack/model/downloader/settings": { "get": { "produces": [ "application/json" @@ -3893,7 +3893,7 @@ "consumes": [ "application/json" ], - "description": "Updates the download directory, source endpoints and tokens. The featuredModelsSync field is ignored; use /ai/model/downloader/featured/sync/status to update automatic synchronization.", + "description": "Updates the download directory, source endpoints and tokens. The featuredModelsSync field is ignored; use /xpack/model/downloader/featured/sync/status to update automatic synchronization.", "parameters": [ { "description": "request", @@ -3936,7 +3936,7 @@ } } }, - "/ai/model/downloader/tasks/cancel": { + "/xpack/model/downloader/tasks/cancel": { "post": { "consumes": [ "application/json" @@ -3993,7 +3993,7 @@ } } }, - "/ai/model/downloader/tasks/remove": { + "/xpack/model/downloader/tasks/remove": { "post": { "consumes": [ "application/json" @@ -4050,7 +4050,7 @@ } } }, - "/ai/model/downloader/tasks/retry": { + "/xpack/model/downloader/tasks/retry": { "post": { "consumes": [ "application/json" @@ -4110,7 +4110,7 @@ } } }, - "/ai/model/downloader/tasks/search": { + "/xpack/model/downloader/tasks/search": { "post": { "consumes": [ "application/json" diff --git a/core/cmd/server/docs/x-log.json b/core/cmd/server/docs/x-log.json index d21b48e7a..09fdbf89e 100644 --- a/core/cmd/server/docs/x-log.json +++ b/core/cmd/server/docs/x-log.json @@ -8,7 +8,7 @@ "formatZH": "更新 Ollama 绑定域名 [domain]", "formatEN": "update Ollama bind domain [domain]" }, - "/ai/model/downloader/featured/sync/status": { + "/xpack/model/downloader/featured/sync/status": { "bodyKeys": [ "value" ], @@ -17,7 +17,7 @@ "formatZH": "更新推荐模型自动同步 [value]", "formatEN": "update featured model automatic synchronization [value]" }, - "/ai/model/downloader/hf/download": { + "/xpack/model/downloader/hf/download": { "bodyKeys": [ "repoID", "revision" @@ -27,7 +27,7 @@ "formatZH": "下载 Hugging Face 模型 [repoID]", "formatEN": "download Hugging Face model [repoID]" }, - "/ai/model/downloader/local/delete": { + "/xpack/model/downloader/local/delete": { "bodyKeys": [ "name" ], @@ -36,7 +36,7 @@ "formatZH": "删除本地模型 [name]", "formatEN": "delete local model [name]" }, - "/ai/model/downloader/modelscope/download": { + "/xpack/model/downloader/modelscope/download": { "bodyKeys": [ "repoID", "revision" @@ -46,7 +46,7 @@ "formatZH": "下载 ModelScope 模型 [repoID]", "formatEN": "download ModelScope model [repoID]" }, - "/ai/model/downloader/settings": { + "/xpack/model/downloader/settings": { "bodyKeys": [ "modelDir" ], @@ -55,7 +55,7 @@ "formatZH": "更新模型下载设置 [modelDir]", "formatEN": "update model downloader settings [modelDir]" }, - "/ai/model/downloader/tasks/cancel": { + "/xpack/model/downloader/tasks/cancel": { "bodyKeys": [ "id" ], @@ -73,7 +73,7 @@ "formatZH": "取消模型下载任务 [repoID]", "formatEN": "cancel model download task [repoID]" }, - "/ai/model/downloader/tasks/remove": { + "/xpack/model/downloader/tasks/remove": { "bodyKeys": [ "id" ], @@ -91,7 +91,7 @@ "formatZH": "移除模型下载任务 [repoID]", "formatEN": "remove model download task [repoID]" }, - "/ai/model/downloader/tasks/retry": { + "/xpack/model/downloader/tasks/retry": { "bodyKeys": [ "id" ], diff --git a/core/middleware/operation.go b/core/middleware/operation.go index 9bef5f5b0..f0c050b58 100644 --- a/core/middleware/operation.go +++ b/core/middleware/operation.go @@ -325,9 +325,6 @@ func loadLogInfo(path string) string { func normalizeOperationPath(reqPath string) string { pathItem := strings.TrimPrefix(reqPath, "/api/v2") pathItem = strings.TrimPrefix(pathItem, "/api/v2/core") - if strings.HasPrefix(pathItem, "/xpack/model/downloader/") { - pathItem = strings.Replace(pathItem, "/xpack/model/downloader/", "/ai/model/downloader/", 1) - } return pathItem } diff --git a/frontend/src/api/interface/model-downloader.ts b/frontend/src/api/interface/model-downloader.ts deleted file mode 100644 index 24322acad..000000000 --- a/frontend/src/api/interface/model-downloader.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { ReqPage, ResPage } from '@/api/interface'; - -export namespace ModelDownloader { - export interface FeaturedModelSearch extends ReqPage { - family: string; - weightProvider: string; - info: string; - } - - export interface FeaturedModelPage extends ResPage { - families: string[]; - weightProviders: string[]; - } - - export interface FeaturedModel { - description: Record | null; - family: string; - repoID: string; - weightProvider: string; - sources: Source[]; - sourceModelIds?: Partial> | null; - architecture: 'MoE' | 'Dense'; - backboneParams: string; - activeParams: string; - engram: string; - weightGB: number; - } - - export type Source = 'huggingface' | 'modelscope'; - export type DownloadType = 'all' | 'tokenizer'; - - export interface ModelSearch extends Partial { - query: string; - sort: 'trending' | 'downloads' | 'updated' | 'created' | 'likes' | 'most_params' | 'least_params'; - limit?: number; - } - - export interface ModelInfoReq { - repoID: string; - revision?: string; - } - - export interface ModelDownload { - downloadType?: DownloadType; - repoID: string; - revision?: string; - } - - export interface Setting { - featuredModelsSync?: 'Enable' | 'Disable'; - modelDir: string; - hfEndpoint: string; - hfToken: string; - modelScopeEndpoint: string; - modelScopeToken: string; - } - - export interface ModelItem { - repoID: string; - name: string; - downloads: number; - likes: number; - size: number; - sizeFormatted: string; - } - - export interface ModelFile { - name: string; - size: number; - sizeFormatted: string; - } - - export interface ModelInfo extends ModelItem { - modelCard: string; - files: ModelFile[]; - } - - export interface TaskSearch extends ReqPage { - ids?: number[]; - includeLocal?: boolean; - downloadType?: DownloadType | ''; - info: string; - status: string; - } - - export interface TaskOperate { - withFile?: boolean; - id: number; - } - - export interface TaskRetry { - id: number; - } - - export interface TaskItem { - localModel?: LocalModelItem; - downloadType: DownloadType; - id: number; - source: string; - repoID: string; - revision: string; - modelName: string; - targetDir: string; - status: string; - progress: number; - totalSize: number; - downloadedSize: number; - errorMessage: string; - createdAt: string; - startedAt: string; - completedAt: string; - canCancel: boolean; - canRetry: boolean; - canRemoveRecord: boolean; - } - - export interface LocalModelOption { - name: string; - organization: string; - path: string; - } - - export interface LocalModelSearch extends ReqPage { - downloadType?: DownloadType | ''; - info: string; - } - - export interface LocalModelItem { - downloadType: DownloadType; - repoID: string; - source: Source; - revision: string; - name: string; - path: string; - size: number; - sizeFormatted: string; - createdAt: string; - } - - export interface LocalModelDelete { - withFile?: boolean; - downloadType?: DownloadType; - name: string; - } -} diff --git a/frontend/src/api/modules/model-downloader.ts b/frontend/src/api/modules/model-downloader.ts deleted file mode 100644 index 99e2b98d1..000000000 --- a/frontend/src/api/modules/model-downloader.ts +++ /dev/null @@ -1,80 +0,0 @@ -import http from '@/api'; -import { ResPage } from '@/api/interface'; -import { ModelDownloader } from '@/api/interface/model-downloader'; -import { TimeoutEnum } from '@/enums/http-enum'; - -export const searchHFModels = (req: ModelDownloader.ModelSearch) => { - return http.post>('/ai/model/downloader/hf/search', req, TimeoutEnum.T_40S); -}; - -export const getHFModelInfo = (req: ModelDownloader.ModelInfoReq) => { - return http.post('/ai/model/downloader/hf/info', req, TimeoutEnum.T_40S); -}; - -export const startHFModelDownload = (req: ModelDownloader.ModelDownload) => { - return http.post('/ai/model/downloader/hf/download', req, TimeoutEnum.T_40S); -}; - -export const searchModelScopeModels = (req: ModelDownloader.ModelSearch) => { - return http.post>( - '/ai/model/downloader/modelscope/search', - req, - TimeoutEnum.T_40S, - ); -}; - -export const getModelScopeModelInfo = (req: ModelDownloader.ModelInfoReq) => { - return http.post('/ai/model/downloader/modelscope/info', req, TimeoutEnum.T_40S); -}; - -export const startModelScopeModelDownload = (req: ModelDownloader.ModelDownload) => { - return http.post('/ai/model/downloader/modelscope/download', req, TimeoutEnum.T_40S); -}; - -export const getModelDownloaderSettings = () => { - return http.get('/ai/model/downloader/settings'); -}; - -export const updateModelDownloaderSettings = (req: ModelDownloader.Setting) => { - return http.post('/ai/model/downloader/settings', req); -}; - -export const pageModelDownloadTasks = (req: ModelDownloader.TaskSearch) => { - return http.post>('/ai/model/downloader/tasks/search', req); -}; - -export const cancelModelDownloadTask = (req: ModelDownloader.TaskOperate) => { - return http.post('/ai/model/downloader/tasks/cancel', req); -}; - -export const retryModelDownloadTask = (req: ModelDownloader.TaskRetry) => { - return http.post('/ai/model/downloader/tasks/retry', req); -}; - -export const removeModelDownloadTask = (req: ModelDownloader.TaskOperate) => { - return http.post('/ai/model/downloader/tasks/remove', req); -}; - -export const pageLocalDownloadedModels = (req: ModelDownloader.LocalModelSearch) => { - return http.post>('/ai/model/downloader/local/search', req); -}; - -export const listLocalModelOptions = () => { - return http.get('/ai/model/downloader/local/options'); -}; - -export const deleteLocalDownloadedModel = (req: ModelDownloader.LocalModelDelete) => { - return http.post('/ai/model/downloader/local/delete', req); -}; - -export const syncFeaturedModels = () => { - return http.post('/ai/model/downloader/featured/sync', {}, TimeoutEnum.T_60S); -}; - -export const pageFeaturedModels = (req: ModelDownloader.FeaturedModelSearch) => { - return http.post('/ai/model/downloader/featured/search', req); -}; - -export const updateFeaturedModelsSync = (value: string) => { - return http.post('/ai/model/downloader/featured/sync/status', { value }); -}; diff --git a/frontend/src/views/ai/model/components/form-section.scss b/frontend/src/components/form-section/index.scss similarity index 100% rename from frontend/src/views/ai/model/components/form-section.scss rename to frontend/src/components/form-section/index.scss diff --git a/frontend/src/views/ai/model/components/form-section.ts b/frontend/src/components/form-section/index.ts similarity index 100% rename from frontend/src/views/ai/model/components/form-section.ts rename to frontend/src/components/form-section/index.ts diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index f9505aea6..8d050c752 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -4021,9 +4021,9 @@ const message = { fileExchange: 'Upgrade to Commercial Edition to transfer files quickly between servers.', app: 'Upgrade to Commercial Edition to view service details, anomaly monitoring, and more in the mobile app.', cluster: 'Upgrade to Commercial Edition to manage MySQL/Postgres/Redis primary-replica clusters.', - vllm: 'Upgrade to Commercial Edition to manage vLLM services in 1Panel with centralized deployment, configuration, lifecycle operations, and task tracking.', + vllm: 'Upgrade to the commercial edition to deploy vLLM instances, monitor services, view logs, and manage daily operations in one place.', 'model-downloader': - 'Upgrade to Commercial Edition to search, download, and manage local model files from HuggingFace, ModelScope, or compatible mirrors with centralized model directories and download tasks.', + 'Upgrade to the commercial edition to browse featured models, manage downloads, reuse vLLM Recipes, and configure download sources and access credentials.', }, clean: { scan: 'Start scanning', @@ -7101,30 +7101,27 @@ const message = { clusterTitle3: 'Replication Status', clusterContent3: 'Displays master-slave replication status and delay information, assisting in troubleshooting synchronization issues', - vllmTitle1: 'Centralized Management', + vllmTitle1: 'Model Deployment', vllmContent1: - 'Bring scattered vLLM services into 1Panel for unified visibility of instances, versions, status, and runtime paths.', - vllmTitle2: 'Fast Deployment', + 'Select a model directory and container image, configure GPUs, ports, and startup parameters, and create vLLM instances.', + vllmTitle2: 'Service Monitoring', vllmContent2: - 'Use a visual form to configure version selection, model mounts, and startup parameters with less manual orchestration.', - vllmTitle3: 'Operational Efficiency', + 'View instance status, request counts, throughput, and latency to monitor model service performance.', + vllmTitle3: 'Daily Operations', vllmContent3: - 'Handle create, edit, start, stop, restart, delete, and task tracking from one page to simplify local model operations.', - vllmTitle4: 'Controlled Configuration', - vllmContent4: - 'Standardize ports, containers, startup commands, and Compose settings through advanced options for easier delivery and later adjustments.', - modelDownloaderTitle1: 'Model Discovery', + 'Start, stop, restart, and delete instances, view logs, open terminals, and track operation tasks.', + modelDownloaderTitle1: 'Featured Models', modelDownloaderContent1: - 'Search HuggingFace model repositories directly and view downloads, likes, model details, and file lists.', - modelDownloaderTitle2: 'Accelerated Downloads', + 'Browse featured models, filter by family and weight provider, view model details, and start downloads.', + modelDownloaderTitle2: 'Model Downloads', modelDownloaderContent2: - 'Configure a HuggingFace acceleration endpoint and HuggingFace Token for the official site, mirrors, and private models.', - modelDownloaderTitle3: 'Task Tracking', + 'Download models from Hugging Face and ModelScope, track progress, cancel or retry tasks, and manage local files.', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - 'Review download queues, task status, progress, and errors, with support for canceling, retrying, and clearing records.', - modelDownloaderTitle4: 'Local Management', + 'Browse and sync official Recipes, manage custom recipes, match models, and reuse deployment parameters.', + modelDownloaderTitle4: 'Configuration', modelDownloaderContent4: - 'Manage local model directories in one place, including paths, sizes, timestamps, and deleting unused model files.', + 'Configure the model download directory, source endpoints, access tokens, and automatic featured model synchronization.', }, node: { master: 'Main Node', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 0653e45f4..199f53b0a 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -4100,9 +4100,9 @@ const message = { app: 'La edición comercial permite ver información de servicio, monitoreo, etc. vía app móvil.', cluster: 'Actualizar a la edición comercial permite gestionar clústeres primario-réplica de MySQL/Postgres/Redis.', - vllm: 'Actualiza a la edición comercial para gestionar servicios vLLM en 1Panel con despliegue, configuración, operaciones del ciclo de vida y seguimiento de tareas centralizados.', + vllm: 'Actualiza a la edición comercial para desplegar vLLM, monitorizar servicios, consultar registros y gestionar las operaciones diarias desde un solo lugar.', 'model-downloader': - 'Actualiza a la edición comercial para buscar, descargar y gestionar archivos de modelos locales desde HuggingFace, ModelScope o espejos compatibles, con directorios de modelos y tareas de descarga centralizados.', + 'Actualiza a la edición comercial para explorar modelos destacados, gestionar descargas, reutilizar vLLM Recipes y configurar los orígenes de descarga y las credenciales de acceso.', offLine: 'Sin conexión', }, clean: { @@ -7222,30 +7222,27 @@ const message = { clusterTitle3: 'Estado de Replicación', clusterContent3: 'Muestra estado de replicación maestro-esclavo y retrasos, ayudando a diagnosticar problemas de sincronización', - modelDownloaderTitle1: 'Descubrimiento de modelos', + modelDownloaderTitle1: 'Modelos destacados', modelDownloaderContent1: - 'Busca repositorios de modelos de HuggingFace directamente y consulta descargas, me gusta, detalles y archivos.', - modelDownloaderTitle2: 'Descargas aceleradas', + 'Explora modelos destacados, filtra por familia y proveedor de pesos, consulta los detalles e inicia descargas.', + modelDownloaderTitle2: 'Descarga de modelos', modelDownloaderContent2: - 'Configura un endpoint de aceleración de HuggingFace y HuggingFace Token para el sitio oficial, espejos y modelos privados.', - modelDownloaderTitle3: 'Seguimiento de tareas', + 'Descarga modelos de Hugging Face y ModelScope, consulta el progreso, cancela o reintenta tareas y gestiona los archivos locales.', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - 'Consulta la cola, el estado, el progreso y los errores de descarga, con soporte para cancelar, reintentar y limpiar registros.', - modelDownloaderTitle4: 'Gestión local', + 'Explora y sincroniza Recipes oficiales, gestiona recetas personalizadas y reutiliza parámetros de despliegue compatibles con cada modelo.', + modelDownloaderTitle4: 'Configuración', modelDownloaderContent4: - 'Gestiona directorios de modelos locales en un solo lugar, incluyendo rutas, tamaños, fechas y eliminación de modelos no usados.', - vllmTitle1: 'Gestión centralizada', + 'Configura el directorio de descarga, las direcciones de origen, los tokens de acceso y la sincronización automática de modelos destacados.', + vllmTitle1: 'Despliegue de modelos', vllmContent1: - 'Centralice la gestión de instancias vLLM distribuidas y supervise versiones, estado de ejecución y directorios de trabajo desde una única plataforma.', - vllmTitle2: 'Despliegue rápido', + 'Selecciona el directorio del modelo y la imagen del contenedor, configura GPU, puertos y parámetros de inicio, y crea instancias de vLLM.', + vllmTitle2: 'Monitorización del servicio', vllmContent2: - 'Configure y despliegue servicios vLLM en minutos mediante formularios visuales, sin necesidad de complejas operaciones manuales.', - vllmTitle3: 'Operación eficiente', + 'Consulta el estado de las instancias, el número de solicitudes, el rendimiento y la latencia del servicio de modelos.', + vllmTitle3: 'Operaciones diarias', vllmContent3: - 'Realice la creación, edición, inicio, detención, reinicio y eliminación de instancias desde una sola interfaz para simplificar la operación diaria.', - vllmTitle4: 'Control avanzado', - vllmContent4: - 'Personalice puertos, contenedores, comandos de arranque y archivos Compose con opciones avanzadas para lograr implementaciones más flexibles y estandarizadas.', + 'Inicia, detén, reinicia y elimina instancias, consulta registros, abre terminales y realiza el seguimiento de las tareas.', }, node: { master: 'Nodo Principal', diff --git a/frontend/src/lang/modules/fa.ts b/frontend/src/lang/modules/fa.ts index 918e13d4b..2dc292fa2 100644 --- a/frontend/src/lang/modules/fa.ts +++ b/frontend/src/lang/modules/fa.ts @@ -3983,9 +3983,9 @@ const message = { fileExchange: 'برای انتقال سریع فایل‌ها بین سرورها به نسخه تجاری ارتقاء دهید.', app: 'برای مشاهده جزئیات سرویس، نظارت بر ناهنجاری‌ها و موارد دیگر در برنامه موبایل به نسخه تجاری ارتقاء دهید.', cluster: 'برای مدیریت خوشه‌های اصلی-برده MySQL/Postgres/Redis به نسخه تجاری ارتقاء دهید.', - vllm: 'برای مدیریت سرویس‌های vLLM در 1Panel با استقرار متمرکز، پیکربندی، عملیات چرخه حیات و ردیابی وظایف به نسخه تجاری ارتقاء دهید.', + vllm: 'برای استقرار vLLM، پایش سرویس‌ها، مشاهده گزارش‌ها و مدیریت یکپارچه عملیات روزانه، به نسخه تجاری ارتقا دهید.', 'model-downloader': - 'برای جستجو، دانلود و مدیریت فایل‌های مدل محلی از HuggingFace، ModelScope یا آینه‌های سازگار با دایرکتوری‌های مدل متمرکز و وظایف دانلود به نسخه تجاری ارتقاء دهید.', + 'برای مرور مدل‌های منتخب، مدیریت دانلودها، استفاده مجدد از vLLM Recipes و تنظیم منابع دانلود و اطلاعات دسترسی، به نسخه تجاری ارتقا دهید.', }, clean: { scan: 'شروع اسکن', @@ -7056,30 +7056,27 @@ const message = { clusterTitle3: 'وضعیت تکرار', clusterContent3: 'وضعیت تکرار اصلی-برده و اطلاعات تأخیر را نمایش می‌دهد و به رفع مشکلات همگام‌سازی کمک می‌کند', - vllmTitle1: 'مدیریت متمرکز', + vllmTitle1: 'استقرار مدل', vllmContent1: - 'سرویس‌های vLLM پراکنده را به 1Panel بیاورید تا دید یکپارچه از نمونه‌ها، نسخه‌ها، وضعیت و مسیرهای زمان اجرا داشته باشید.', - vllmTitle2: 'استقرار سریع', + 'پوشه مدل و ایمیج کانتینر را انتخاب کنید و با تنظیم GPU، درگاه‌ها و پارامترهای راه‌اندازی، نمونه‌های vLLM بسازید.', + vllmTitle2: 'پایش سرویس', vllmContent2: - 'از یک فرم بصری برای پیکربندی انتخاب نسخه، نصب مدل و پارامترهای راه‌اندازی با هماهنگی دستی کمتر استفاده کنید.', - vllmTitle3: 'کارایی عملیاتی', + 'وضعیت نمونه‌ها، تعداد درخواست‌ها، توان عملیاتی و تأخیر را برای پایش عملکرد سرویس مدل مشاهده کنید.', + vllmTitle3: 'عملیات روزانه', vllmContent3: - 'عملیات ایجاد، ویرایش، شروع، توقف، راه‌اندازی مجدد، حذف و ردیابی وظایف را از یک صفحه مدیریت کنید تا عملیات مدل محلی ساده‌تر شود.', - vllmTitle4: 'پیکربندی کنترل شده', - vllmContent4: - 'پورت‌ها، کانتینرها، دستورات راه‌اندازی و تنظیمات Compose را از طریق گزینه‌های پیشرفته استاندارد کنید تا تحویل و تنظیمات بعدی آسان‌تر شود.', - modelDownloaderTitle1: 'کشف مدل', + 'نمونه‌ها را راه‌اندازی، متوقف، بازراه‌اندازی یا حذف کنید، گزارش‌ها را ببینید، ترمینال را باز کنید و کارهای عملیاتی را پیگیری کنید.', + modelDownloaderTitle1: 'مدل‌های منتخب', modelDownloaderContent1: - 'مستقیماً مخازن مدل HuggingFace را جستجو کنید و دانلودها، لایک‌ها، جزئیات مدل و لیست فایل‌ها را مشاهده کنید.', - modelDownloaderTitle2: 'دانلود شتاب‌دهنده', + 'مدل‌های منتخب را مرور کنید، بر اساس خانواده و ارائه‌دهنده وزن فیلتر کنید، جزئیات را ببینید و دانلود را آغاز کنید.', + modelDownloaderTitle2: 'دانلود مدل', modelDownloaderContent2: - 'یک نقطه پایانی شتاب HuggingFace و توکن HuggingFace را برای سایت رسمی، آینه‌ها و مدل‌های خصوصی پیکربندی کنید.', - modelDownloaderTitle3: 'ردیابی وظایف', + 'مدل‌ها را از Hugging Face و ModelScope دانلود کنید، پیشرفت را ببینید، کارها را لغو یا دوباره اجرا کنید و فایل‌های محلی را مدیریت کنید.', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - 'صف‌های دانلود، وضعیت وظایف، پیشرفت و خطاها را مرور کنید و از لغو، تلاش مجدد و پاک‌سازی رکوردها پشتیبانی کنید.', - modelDownloaderTitle4: 'مدیریت محلی', + 'Recipes رسمی را مرور و همگام‌سازی کنید، دستورهای سفارشی را مدیریت کنید و پارامترهای استقرار متناسب با مدل را دوباره به کار ببرید.', + modelDownloaderTitle4: 'پیکربندی', modelDownloaderContent4: - 'دایرکتوری‌های مدل محلی را در یک مکان مدیریت کنید، از جمله مسیرها، اندازه‌ها، مهرهای زمانی و حذف فایل‌های مدل استفاده نشده.', + 'پوشه دانلود مدل، نشانی منابع، توکن‌های دسترسی و همگام‌سازی خودکار مدل‌های منتخب را تنظیم کنید.', }, node: { master: 'گره اصلی', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 3b1e1d76a..a95106b75 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -4000,9 +4000,9 @@ const message = { '商用版にアップグレードすると、PHP 拡張機能テンプレートを1つ以上のターゲットノードに同期できます。', fileExchange: '商用版にアップグレードすると、複数サーバー間でファイルをすばやく転送できます。', cluster: '商用版にアップグレードすると、MySQL/Postgres/Redis のプライマリ/レプリカ構成を管理できます。', - vllm: '商用版にアップグレードすると、1Panel で vLLM サービスのデプロイ、設定、ライフサイクル操作、タスク追跡を一元管理できます。', + vllm: '商用版にアップグレードすると、vLLM のデプロイ、稼働監視、ログ確認、日常運用を一元管理できます。', 'model-downloader': - '商用版にアップグレードすると、HuggingFace、ModelScope、または互換ミラーからローカルモデルファイルを検索、ダウンロード、管理できます。モデルディレクトリとダウンロードタスクを一元管理できます。', + '商用版にアップグレードすると、おすすめモデルの閲覧、ダウンロードの管理、vLLM Recipes の再利用、ダウンロード元と認証情報の設定ができます。', exceptionalHelper: 'ライセンス同期検証が異常です。手動で同期して商用版機能が正常に動作していることを確認してください。詳細: ', tamperHelper: '操作失敗。ファイル/フォルダの改ざん保護を確認してください。', @@ -7086,30 +7086,27 @@ const message = { clusterTitle3: 'レプリケーション状態', clusterContent3: 'マスタースレーブレプリケーション状態と遅延情報を表示し、同期の問題を解決するのに役立ちます', - modelDownloaderTitle1: 'モデル検索', + modelDownloaderTitle1: 'おすすめモデル', modelDownloaderContent1: - 'HuggingFace のモデルリポジトリを直接検索し、ダウンロード数、いいね、モデル詳細、ファイル一覧を確認できます。', - modelDownloaderTitle2: '高速ダウンロード', + 'おすすめモデルをシリーズや重みの提供元で絞り込み、詳細を確認してダウンロードできます。', + modelDownloaderTitle2: 'モデルのダウンロード', modelDownloaderContent2: - 'HuggingFace アクセラレーションエンドポイントと HuggingFace Token を設定し、公式サイト、ミラー、プライベートモデルに対応します。', - modelDownloaderTitle3: 'タスク追跡', + 'Hugging Face と ModelScope からモデルをダウンロードし、進捗の確認、キャンセル、再試行、ローカルファイルの管理ができます。', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - 'ダウンロードキュー、タスク状態、進捗、エラーを一元確認し、キャンセル、再試行、記録の削除を行えます。', - modelDownloaderTitle4: 'ローカル管理', + '公式 Recipes の閲覧・同期、カスタムレシピの管理、モデルに合うレシピの検索、デプロイパラメーターの再利用ができます。', + modelDownloaderTitle4: '設定', modelDownloaderContent4: - 'ローカルモデルディレクトリを一元管理し、パス、サイズ、日時の確認と不要なモデルファイルの削除を行えます。', - vllmTitle1: '一元管理', + 'モデルの保存先、ダウンロード元の URL、アクセストークン、おすすめモデルの自動同期を設定できます。', + vllmTitle1: 'モデルのデプロイ', vllmContent1: - '分散した vLLM サービスを 1Panel に統合し、インスタンス、バージョン、状態、実行ディレクトリを一元的に確認できます。', - vllmTitle2: '高速デプロイ', + 'モデルのディレクトリとコンテナーイメージを選び、GPU、ポート、起動パラメーターを設定して vLLM インスタンスを作成できます。', + vllmTitle2: '稼働監視', vllmContent2: - 'ビジュアルフォームでバージョン選択、モデルディレクトリマウント、起動パラメータ設定を完了し、手動構成コストを削減します。', - vllmTitle3: '運用効率', + 'インスタンスの状態、リクエスト数、スループット、レイテンシーを確認し、モデルサービスの稼働状況を把握できます。', + vllmTitle3: '日常運用', vllmContent3: - '同じページで作成、編集、起動、停止、再起動、削除、タスク追跡を完結し、ローカルモデルサービスの保守効率を向上させます。', - vllmTitle4: '設定の可控性', - vllmContent4: - '高度な設定でポート、コンテナ、起動コマンド、Compose 設定を一元管理し、標準化されたデリバリーと后续調整を容易にします。', + 'インスタンスの起動・停止・再起動・削除、ログの確認、ターミナルへのアクセス、操作タスクの追跡ができます。', }, node: { master: '主ノード', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 7eb029d39..6e29c2beb 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -3931,9 +3931,9 @@ const message = { fileExchange: '상용 버전으로 업그레이드하면 여러 서버 간에 파일을 빠르게 전송할 수 있습니다.', app: '상용 버전으로 업그레이드하면 모바일 앱에서 서비스 정보와 이상 모니터링을 확인할 수 있습니다.', cluster: '상용 버전으로 업그레이드하면 MySQL/Postgres/Redis 기본-복제 클러스터를 관리할 수 있습니다.', - vllm: '상용 버전으로 업그레이드하면 1Panel에서 vLLM 서비스의 배포, 구성, 수명 주기 작업 및 작업 추적을 중앙에서 관리할 수 있습니다.', + vllm: '상용 버전으로 업그레이드하면 vLLM 배포, 서비스 모니터링, 로그 조회 및 일상 운영을 한곳에서 관리할 수 있습니다.', 'model-downloader': - '상용 버전으로 업그레이드하면 HuggingFace, ModelScope 또는 호환 미러에서 로컬 모델 파일을 검색, 다운로드 및 관리하고 모델 디렉터리와 다운로드 작업을 통합 관리할 수 있습니다.', + '상용 버전으로 업그레이드하면 추천 모델 조회, 다운로드 관리, vLLM Recipes 재사용 및 다운로드 소스와 인증 정보 설정을 이용할 수 있습니다.', exceptionalHelper: '라이선스 동기화 검증이 비정상입니다. 수동으로 동기화해 상용 버전 기능이 정상적으로 동작하는지 확인하세요. 상세: ', tamperHelper: '작업 실패, 파일/폴더에 변조 방지가 활성화되어 있습니다. 확인 후 다시 시도하세요!', @@ -6941,30 +6941,27 @@ const message = { clusterTitle3: '복제 상태', clusterContent3: '마스터-슬레이브 복제 상태 및 지연 정보를 표시하여 동기화 문제를 해결하는 데 도움을 줍니다', - modelDownloaderTitle1: '모델 탐색', + modelDownloaderTitle1: '추천 모델', modelDownloaderContent1: - 'HuggingFace 모델 저장소를 직접 검색하고 다운로드 수, 좋아요, 모델 상세 정보, 파일 목록을 확인합니다.', - modelDownloaderTitle2: '가속 다운로드', + '추천 모델을 모델 계열과 가중치 제공자로 필터링하고, 모델 정보를 확인하여 다운로드할 수 있습니다.', + modelDownloaderTitle2: '모델 다운로드', modelDownloaderContent2: - 'HuggingFace 가속 엔드포인트와 HuggingFace Token을 설정하여 공식 사이트, 미러, 비공개 모델에 대응합니다.', - modelDownloaderTitle3: '작업 추적', + 'Hugging Face와 ModelScope에서 모델을 다운로드하고 진행 상황 확인, 취소, 재시도 및 로컬 파일 관리를 수행합니다.', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - '다운로드 대기열, 작업 상태, 진행률, 오류를 한곳에서 확인하고 취소, 재시도, 기록 정리를 지원합니다.', - modelDownloaderTitle4: '로컬 관리', + '공식 Recipes를 조회하고 동기화하며, 사용자 정의 레시피를 관리하고 모델에 맞는 배포 매개변수를 재사용합니다.', + modelDownloaderTitle4: '설정', modelDownloaderContent4: - '로컬 모델 디렉토리를 중앙에서 관리하고 경로, 크기, 시간을 확인하며 불필요한 모델 파일을 삭제합니다.', - vllmTitle1: '통합 관리', + '모델 다운로드 디렉터리, 소스 주소, 액세스 토큰 및 추천 모델 자동 동기화를 설정합니다.', + vllmTitle1: '모델 배포', vllmContent1: - '분산된 vLLM 서비스를 1Panel에 통합하여 인스턴스、버전、상태、실행 디렉토리를 집중 조회할 수 있습니다.', - vllmTitle2: '빠른 배포', + '모델 디렉터리와 컨테이너 이미지를 선택하고 GPU, 포트 및 시작 매개변수를 설정하여 vLLM 인스턴스를 생성합니다.', + vllmTitle2: '서비스 모니터링', vllmContent2: - '시각적 양식으로 버전 선택、모델 디렉토리 마운트、시작 매개변수 설정을 완료하여 수동 구성 비용을 줄입니다.', - vllmTitle3: '운영 효율성', + '인스턴스 상태, 요청 수, 처리량 및 지연 시간을 확인하여 모델 서비스의 운영 상태를 파악합니다.', + vllmTitle3: '일상 운영', vllmContent3: - '같은 페이지에서 생성、편집、시작、중지、재시작、삭제、작업 추적을 완료하여 로컬 모델 서비스 유지보수 효율을 향상시킵니다.', - vllmTitle4: '구성 제어', - vllmContent4: - '고급 설정으로 포트、컨테이너、시작 명령、Compose 구성을 통합 관리하여 표준화된 배포와 후속 조정을 용이하게 합니다.', + '인스턴스 시작, 중지, 재시작 및 삭제를 수행하고 로그 조회, 터미널 접속 및 작업 추적을 지원합니다.', }, node: { master: '주 노드', diff --git a/frontend/src/lang/modules/lo.ts b/frontend/src/lang/modules/lo.ts index 967a7ba8a..15ae8b429 100644 --- a/frontend/src/lang/modules/lo.ts +++ b/frontend/src/lang/modules/lo.ts @@ -3908,9 +3908,9 @@ const message = { fileExchange: 'ອັບເກຣດເປັນເວີຊັນ Commercial ເພື່ອໂອນຍ້າຍໄຟລ໌ລະຫວ່າງເຊີບເວີຢ່າງວ່ອງໄວ.', app: 'ອັບເກຣດເປັນເວີຊັນ Commercial ເພື່ອເບິ່ງລາຍລະອຽດບໍລິການ, ການຕິດຕາມສິ່ງຜິດປົກກະຕິ ແລະ ອື່ນໆໃນແອັບມືຖື.', cluster: 'ອັບເກຣດເປັນເວີຊັນ Commercial ເພື່ອຈັດການ MySQL/Postgres/Redis primary-replica clusters.', - vllm: 'ອັບເກຣດເປັນເວີຊັນ Commercial ເພື່ອຈັດການບໍລິການ vLLM ໃນ 1Panel ດ້ວຍການຕິດຕັ້ງ, ຕັ້ງຄ່າ, ການເຮັດວຽກ lifecycle ແລະ ການຕິດຕາມໜ້າວຽກແບບລວມສູນ.', + vllm: 'ອັບເກຣດເປັນລຸ້ນທຸລະກິດເພື່ອຕິດຕັ້ງ vLLM, ຕິດຕາມບໍລິການ, ເບິ່ງບັນທຶກ ແລະຈັດການວຽກປະຈຳວັນໃນບ່ອນດຽວ.', 'model-downloader': - 'ອັບເກຣດເປັນເວີຊັນ Commercial ເພື່ອຄົ້ນຫາ, ດາວໂຫຼດ ແລະ ຈັດການໄຟລ໌ໂມເດວທ້ອງຖິ່ນຈາກ HuggingFace, ModelScope ຫຼື mirror ທີ່ຮອງຮັບ ພ້ອມກັບໄດເຣັກທໍຣີໂມເດວ ແລະ ໜ້າວຽກດາວໂຫຼດແບບລວມສູນ.', + 'ອັບເກຣດເປັນລຸ້ນທຸລະກິດເພື່ອເບິ່ງໂມເດລແນະນຳ, ຈັດການການດາວໂຫຼດ, ໃຊ້ vLLM Recipes ຄືນ ແລະຕັ້ງຄ່າແຫຼ່ງດາວໂຫຼດກັບຂໍ້ມູນຢືນຢັນການເຂົ້າເຖິງ.', }, clean: { scan: 'ເລີ່ມສະແກນ', @@ -6878,23 +6878,25 @@ const message = { clusterContent2: 'ຈັດການຫຼາຍໂນດ Master-Slave ໃນໜ້າດຽວ, ເບິ່ງບົດບາດ ແລະ ສະຖານະການທຳງານ.', clusterTitle3: 'ສະຖານະການກັອບປີ້ຂໍ້ມູນ', clusterContent3: 'ສະແດງສະຖານະການ Replication ແລະ ຂໍ້ມູນການດີເລ (Delay) ເພື່ອຊ່ວຍແກ້ໄຂບັນຫາ.', - vllmTitle1: 'ຈັດການແບບລວມສູນ', - vllmContent1: 'ລວມບໍລິການ vLLM ທີ່ກະຈັດກະຈາຍເຂົ້າມາໃນ 1Panel ເພື່ອເບິ່ງສະຖານະ, ເວີຊັນ ແລະ ເສັ້ນທາງທຳງານ.', - vllmTitle2: 'ຕິດຕັ້ງວ່ອງໄວ', - vllmContent2: 'ໃຊ້ຟອມເພື່ອເລືອກເວີຊັນ, ເທົາມໍເດລ ແລະ ຕັ້ງຄ່າການເລີ່ມຕົ້ນໄດ້ງ່າຍ.', - vllmTitle3: 'ປະສິດທິພາບການດຳເນີນງານ', - vllmContent3: 'ຈັດການສ້າງ, ແກ້ໄຂ, ເລີ່ມ, ຢຸດ ແລະ ລຶບ ໄດ້ໃນໜ້າດຽວ.', - vllmTitle4: 'ຄວບຄຸມການຕັ້ງຄ່າ', - vllmContent4: 'ກຳນົດພອດ, ຄອນເທນເນີ ແລະ ຄຳສັ່ງເລີ່ມຕົ້ນໃຫ້ເປັນມາດຕະຖານ.', - modelDownloaderTitle1: 'ຄົ້ນຫາມໍເດລ', - modelDownloaderContent1: 'ຄົ້ນຫາຄັງມໍເດລ HuggingFace ໂດຍກົງ ແລະ ເບິ່ງຍອດດາວໂຫຼດ, ໄລ້ ແລະ ລາຍລະອຽດໄຟລ໌.', - modelDownloaderTitle2: 'ດາວໂຫຼດເລັ່ງດ່ວນ', + vllmTitle1: 'ຕິດຕັ້ງໂມເດລ', + vllmContent1: + 'ເລືອກໂຟນເດີໂມເດລ ແລະອິມເມຈຄອນເທນເນີ, ຕັ້ງຄ່າ GPU, ພອດ ແລະພາລາມິເຕີເລີ່ມຕົ້ນ ເພື່ອສ້າງອິນສະແຕນ vLLM.', + vllmTitle2: 'ຕິດຕາມບໍລິການ', + vllmContent2: 'ເບິ່ງສະຖານະອິນສະແຕນ, ຈຳນວນຄຳຮ້ອງຂໍ, ອັດຕາປະມວນຜົນ ແລະຄວາມລ່າຊ້າຂອງບໍລິການໂມເດລ.', + vllmTitle3: 'ການດູແລປະຈຳວັນ', + vllmContent3: 'ເລີ່ມ, ຢຸດ, ເລີ່ມໃໝ່ ແລະລຶບອິນສະແຕນ, ເບິ່ງບັນທຶກ, ເປີດເທີມິນັລ ແລະຕິດຕາມວຽກ.', + modelDownloaderTitle1: 'ໂມເດລແນະນຳ', + modelDownloaderContent1: + 'ເບິ່ງໂມເດລແນະນຳ, ກັ່ນຕອງຕາມຕະກູນໂມເດລ ແລະຜູ້ໃຫ້ນ້ຳໜັກ, ເບິ່ງລາຍລະອຽດ ແລະເລີ່ມດາວໂຫຼດ.', + modelDownloaderTitle2: 'ດາວໂຫຼດໂມເດລ', modelDownloaderContent2: - 'ຕັ້ງຄ່າ ຈຸດເຊື່ອມຕໍ່ເລັ່ງຄວາມໄວ HuggingFace ແລະ HuggingFace Token ສຳລັບມໍເດລສ່ວນຕົວ.', - modelDownloaderTitle3: 'ຕິດຕາມງານ', - modelDownloaderContent3: 'ກວດສອບຄິວດາວໂຫຼດ, ສະຖານະ, ຄວາມຄືບໜ້າ ແລະ ຂໍ້ຜິດພາດ.', - modelDownloaderTitle4: 'ຈັດການໃນເຄື່ອງ', - modelDownloaderContent4: 'ຈັດການໄດເຣັກທໍຣີມໍເດລໃນເຄື່ອງ, ລວມມີຂະໜາດ, ເວລາ ແລະ ການລຶບໄຟລ໌ທີ່ບໍ່ໄດ້ໃຊ້.', + 'ດາວໂຫຼດໂມເດລຈາກ Hugging Face ແລະ ModelScope, ຕິດຕາມຄວາມຄືບໜ້າ, ຍົກເລີກ ຫຼືລອງໃໝ່ ແລະຈັດການໄຟລ໌ໃນເຄື່ອງ.', + modelDownloaderTitle3: 'vLLM Recipes', + modelDownloaderContent3: + 'ເບິ່ງ ແລະຊິງຄ໌ Recipes ທາງການ, ຈັດການສູດທີ່ກຳນົດເອງ, ຈັບຄູ່ໂມເດລ ແລະນຳພາລາມິເຕີການຕິດຕັ້ງມາໃຊ້ຄືນ.', + modelDownloaderTitle4: 'ການຕັ້ງຄ່າ', + modelDownloaderContent4: + 'ຕັ້ງຄ່າໂຟນເດີດາວໂຫຼດໂມເດລ, ທີ່ຢູ່ແຫຼ່ງດາວໂຫຼດ, ໂທເຄັນເຂົ້າເຖິງ ແລະການຊິງຄ໌ໂມເດລແນະນຳອັດຕະໂນມັດ.', }, node: { master: 'ໂນດຫຼັກ (Master)', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 3218dbf33..2be27347d 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -4088,9 +4088,9 @@ const message = { fileExchange: 'Naik taraf ke Edisi Komersial untuk memindahkan fail dengan pantas antara pelayan.', app: 'Naik taraf ke Edisi Komersial untuk melihat maklumat perkhidmatan dan pemantauan anomali dalam aplikasi mudah alih.', cluster: 'Naik taraf ke Edisi Komersial untuk mengurus kelompok utama-replika MySQL/Postgres/Redis.', - vllm: 'Naik taraf ke Edisi Komersial untuk mengurus perkhidmatan vLLM secara berpusat dalam 1Panel, termasuk penggunaan, konfigurasi, operasi kitar hayat dan penjejakan tugas.', + vllm: 'Naik taraf kepada edisi komersial untuk menggunakan instans vLLM, memantau perkhidmatan, melihat log dan mengurus operasi harian di satu tempat.', 'model-downloader': - 'Naik taraf kepada edisi komersial untuk mencari, memuat turun dan mengurus fail model tempatan daripada HuggingFace, ModelScope atau cermin serasi, dengan direktori model dan tugasan muat turun berpusat.', + 'Naik taraf kepada edisi komersial untuk menyemak model pilihan, mengurus muat turun, menggunakan semula vLLM Recipes serta menetapkan sumber muat turun dan kelayakan akses.', exceptionalHelper: 'Pengesahan penyegerakan lesen tidak normal. Klik butang sync secara manual untuk pastikan fungsi Edisi Komersial berjalan baik. butiran: ', tamperHelper: 'Operasi gagal, fail atau folder mempunyai perlindungan gangguan. Sila semak dan cuba lagi!', @@ -7205,30 +7205,27 @@ const message = { clusterTitle3: 'Status Replikasi', clusterContent3: 'Memaparkan status replikasi utama-hamba dan maklumat kelewatan, membantu menyelesaikan masalah sinkronisasi', - modelDownloaderTitle1: 'Penemuan Model', + modelDownloaderTitle1: 'Model Pilihan', modelDownloaderContent1: - 'Cari repositori model HuggingFace secara langsung dan lihat muat turun, suka, butiran model dan senarai fail.', - modelDownloaderTitle2: 'Muat Turun Dipercepat', + 'Semak model pilihan, tapis mengikut keluarga dan penyedia pemberat, lihat butiran model dan mulakan muat turun.', + modelDownloaderTitle2: 'Muat Turun Model', modelDownloaderContent2: - 'Konfigurasikan titik akhir pecutan HuggingFace dan HuggingFace Token untuk laman rasmi, cermin dan model persendirian.', - modelDownloaderTitle3: 'Penjejakan Tugas', + 'Muat turun model daripada Hugging Face dan ModelScope, pantau kemajuan, batalkan atau cuba semula tugas dan urus fail setempat.', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - 'Semak baris gilir muat turun, status tugas, kemajuan dan ralat, dengan sokongan batal, cuba semula dan bersihkan rekod.', - modelDownloaderTitle4: 'Pengurusan Tempatan', + 'Semak dan segerakkan Recipes rasmi, urus resipi tersuai, padankan model dan guna semula parameter penggunaan.', + modelDownloaderTitle4: 'Konfigurasi', modelDownloaderContent4: - 'Urus direktori model tempatan di satu tempat, termasuk laluan, saiz, masa dan pemadaman fail model yang tidak digunakan.', - vllmTitle1: 'Pengurusan Berpusat', + 'Tetapkan direktori muat turun model, alamat sumber, token akses dan penyegerakan automatik model pilihan.', + vllmTitle1: 'Penggunaan Model', vllmContent1: - 'Integrasikan instans vLLM yang diedarkan ke dalam 1Panel dan pantau versi, status operasi serta direktori kerja secara berpusat.', - vllmTitle2: 'Penerapan Pantas', + 'Pilih direktori model dan imej kontena, tetapkan GPU, port dan parameter permulaan, kemudian cipta instans vLLM.', + vllmTitle2: 'Pemantauan Perkhidmatan', vllmContent2: - 'Gunakan borang visual untuk memilih versi, memasang direktori model dan mengkonfigurasi parameter permulaan, sekali gus mengurangkan kerumitan konfigurasi manual.', - vllmTitle3: 'Operasi yang Cekap', + 'Lihat status instans, jumlah permintaan, daya pemprosesan dan kependaman untuk memantau prestasi perkhidmatan model.', + vllmTitle3: 'Operasi Harian', vllmContent3: - 'Urus penciptaan, penyuntingan, permulaan, pemberhentian, mulakan semula, penghapusan instans serta pemantauan tugas dalam satu antara muka bagi meningkatkan kecekapan operasi perkhidmatan model tempatan.', - vllmTitle4: 'Konfigurasi Fleksibel', - vllmContent4: - 'Urus port, kontena, arahan permulaan dan konfigurasi Compose secara berpusat melalui tetapan lanjutan untuk memudahkan penerapan piawai serta pelarasan selepas pelaksanaan.', + 'Mulakan, hentikan, mulakan semula dan padam instans, lihat log, buka terminal dan jejak tugas operasi.', }, node: { master: 'Nod Utama', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 63414a361..eab24403d 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -4094,9 +4094,9 @@ const message = { fileExchange: 'Atualize para a edição comercial para transferir arquivos rapidamente entre vários servidores.', app: 'Faça upgrade para a edição comercial para visualizar informações do serviço e monitoramento de anomalias no aplicativo móvel.', cluster: 'Atualize para a edição comercial para gerenciar clusters primário-réplica de MySQL/Postgres/Redis.', - vllm: 'Atualize para a edição comercial para gerenciar serviços vLLM de forma centralizada no 1Panel, incluindo implantação, configuração, operações de ciclo de vida e acompanhamento de tarefas.', + vllm: 'Atualize para a edição comercial para implantar vLLM, monitorar serviços, consultar logs e gerenciar as operações diárias em um só lugar.', 'model-downloader': - 'Atualize para a edição comercial para pesquisar, baixar e gerenciar arquivos de modelos locais do HuggingFace, ModelScope ou mirrors compatíveis, com diretórios de modelos e tarefas de download centralizados.', + 'Atualize para a edição comercial para explorar modelos em destaque, gerenciar downloads, reutilizar vLLM Recipes e configurar fontes de download e credenciais de acesso.', exceptionalHelper: 'A verificação da sincronização da licença está anormal. Clique em sincronizar manualmente para garantir o funcionamento da edição comercial. detalhe: ', tamperHelper: @@ -7243,30 +7243,27 @@ const message = { clusterTitle3: 'Estado de Replicação', clusterContent3: 'Exibe o estado de replicação mestre-escravo e informações de atraso, auxiliando na resolução de problemas de sincronização', - modelDownloaderTitle1: 'Descoberta de modelos', + modelDownloaderTitle1: 'Modelos em destaque', modelDownloaderContent1: - 'Pesquise repositórios de modelos do HuggingFace diretamente e veja downloads, curtidas, detalhes do modelo e lista de arquivos.', - modelDownloaderTitle2: 'Downloads acelerados', + 'Explore modelos em destaque, filtre por família e provedor de pesos, consulte os detalhes e inicie downloads.', + modelDownloaderTitle2: 'Download de modelos', modelDownloaderContent2: - 'Configure um endpoint de aceleração do HuggingFace e HuggingFace Token para o site oficial, espelhos e modelos privados.', - modelDownloaderTitle3: 'Acompanhamento de tarefas', + 'Baixe modelos do Hugging Face e ModelScope, acompanhe o progresso, cancele ou repita tarefas e gerencie arquivos locais.', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - 'Veja filas de download, status, progresso e erros, com suporte para cancelar, tentar novamente e limpar registros.', - modelDownloaderTitle4: 'Gerenciamento local', + 'Explore e sincronize Recipes oficiais, gerencie receitas personalizadas e reutilize parâmetros de implantação compatíveis com cada modelo.', + modelDownloaderTitle4: 'Configuração', modelDownloaderContent4: - 'Gerencie diretórios de modelos locais em um só lugar, incluindo caminhos, tamanhos, horários e exclusão de modelos não usados.', - vllmTitle1: 'Gestão centralizada', + 'Configure o diretório de download, os endereços das fontes, os tokens de acesso e a sincronização automática dos modelos em destaque.', + vllmTitle1: 'Implantação de modelos', vllmContent1: - 'Integre instâncias vLLM distribuídas ao 1Panel e monitore versões, status operacional e diretórios de execução de forma centralizada.', - vllmTitle2: 'Implantação rápida', + 'Selecione o diretório do modelo e a imagem do contêiner, configure GPUs, portas e parâmetros de inicialização e crie instâncias do vLLM.', + vllmTitle2: 'Monitoramento do serviço', vllmContent2: - 'Utilize formulários visuais para selecionar versões, montar diretórios de modelos e configurar parâmetros de inicialização, reduzindo a complexidade da configuração manual.', - vllmTitle3: 'Eficiência operacional', + 'Consulte o estado das instâncias, a quantidade de requisições, a vazão e a latência do serviço de modelos.', + vllmTitle3: 'Operações diárias', vllmContent3: - 'Gerencie a criação, edição, inicialização, parada, reinicialização, exclusão de instâncias e o acompanhamento de tarefas em uma única interface, aumentando a eficiência operacional dos serviços de modelos locais.', - vllmTitle4: 'Configuração flexível', - vllmContent4: - 'Gerencie portas, contêineres, comandos de inicialização e configurações do Compose de forma unificada por meio de opções avançadas, facilitando implantações padronizadas e ajustes posteriores.', + 'Inicie, pare, reinicie e exclua instâncias, consulte logs, abra terminais e acompanhe tarefas operacionais.', }, node: { master: 'Nó Principal', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index d10e492e2..168e7e7a1 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -4070,9 +4070,9 @@ const message = { fileExchange: 'Обновите до Коммерческой версии, чтобы быстро передавать файлы между несколькими серверами.', app: 'Обновите до коммерческой версии, чтобы просматривать информацию о сервисах, мониторинг аномалий и т.д. через мобильное приложение.', cluster: 'Обновление до коммерческой версии позволяет управлять кластерами мастер-слейв MySQL/Postgres/Redis.', - vllm: 'Обновите до коммерческой версии, чтобы централизованно управлять сервисами vLLM в 1Panel: развертыванием, настройкой, жизненным циклом и отслеживанием задач.', + vllm: 'Перейдите на коммерческую версию для развёртывания vLLM, мониторинга сервисов, просмотра журналов и централизованного повседневного управления.', 'model-downloader': - 'Обновите до коммерческой версии, чтобы искать, загружать и управлять локальными файлами моделей из HuggingFace, ModelScope или совместимых зеркал, централизованно управляя каталогами моделей и задачами загрузки.', + 'Перейдите на коммерческую версию, чтобы просматривать рекомендуемые модели, управлять загрузками, повторно использовать vLLM Recipes и настраивать источники загрузки и данные доступа.', exceptionalHelper: 'Проверка синхронизации лицензии выполнена с ошибкой. Нажмите синхронизацию вручную для корректной работы коммерческой версии. Детали: ', tamperHelper: 'Операция не выполнена: для файла или папки включена защита от изменений. Проверьте и повторите!', @@ -7215,30 +7215,27 @@ const message = { clusterTitle3: 'Состояние репликации', clusterContent3: 'Отображает состояние репликации мастер-слейв и информацию о задержке, помогая в устранении проблем синхронизации', - modelDownloaderTitle1: 'Поиск моделей', + modelDownloaderTitle1: 'Рекомендуемые модели', modelDownloaderContent1: - 'Ищите репозитории моделей HuggingFace напрямую и просматривайте загрузки, лайки, сведения о модели и список файлов.', - modelDownloaderTitle2: 'Ускоренные загрузки', + 'Просматривайте рекомендуемые модели, фильтруйте их по семейству и поставщику весов, изучайте сведения и запускайте загрузку.', + modelDownloaderTitle2: 'Загрузка моделей', modelDownloaderContent2: - 'Настройте endpoint ускорения HuggingFace и HuggingFace Token для официального сайта, зеркал и приватных моделей.', - modelDownloaderTitle3: 'Отслеживание задач', + 'Загружайте модели из Hugging Face и ModelScope, отслеживайте прогресс, отменяйте или повторяйте задачи и управляйте локальными файлами.', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - 'Просматривайте очередь загрузок, статус, прогресс и ошибки, отменяйте, повторяйте и очищайте записи.', - modelDownloaderTitle4: 'Локальное управление', + 'Просматривайте и синхронизируйте официальные Recipes, управляйте собственными рецептами и повторно используйте параметры развёртывания для подходящих моделей.', + modelDownloaderTitle4: 'Настройки', modelDownloaderContent4: - 'Управляйте локальными каталогами моделей в одном месте: пути, размеры, время и удаление неиспользуемых файлов моделей.', - vllmTitle1: 'Централизованное управление', + 'Настройте каталог загрузки моделей, адреса источников, токены доступа и автоматическую синхронизацию рекомендуемых моделей.', + vllmTitle1: 'Развёртывание моделей', vllmContent1: - 'Интегрируйте распределённые экземпляры vLLM в 1Panel и централизованно отслеживайте версии, состояние работы и рабочие каталоги.', - vllmTitle2: 'Быстрое развёртывание', + 'Выберите каталог модели и образ контейнера, настройте GPU, порты и параметры запуска и создайте экземпляры vLLM.', + vllmTitle2: 'Мониторинг сервиса', vllmContent2: - 'Выбирайте версии, подключайте каталоги моделей и настраивайте параметры запуска через визуальную форму, сокращая необходимость ручной настройки.', - vllmTitle3: 'Высокая операционная эффективность', + 'Просматривайте состояние экземпляров, число запросов, пропускную способность и задержку сервиса моделей.', + vllmTitle3: 'Повседневное управление', vllmContent3: - 'Создавайте, редактируйте, запускайте, останавливайте, перезапускайте и удаляйте экземпляры, а также отслеживайте задачи через единый интерфейс, повышая эффективность управления локальными сервисами моделей.', - vllmTitle4: 'Гибкая настройка', - vllmContent4: - 'Управляйте портами, контейнерами, командами запуска и конфигурациями Compose через расширенные параметры, упрощая стандартизированное развёртывание и последующую настройку.', + 'Запускайте, останавливайте, перезапускайте и удаляйте экземпляры, просматривайте журналы, открывайте терминалы и отслеживайте задачи.', }, node: { master: 'Главный узел', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 1d257047f..232cd6b3d 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -4084,9 +4084,9 @@ const message = { fileExchange: 'Ticari sürüme yükselterek birden fazla sunucu arasında dosyaları hızlıca aktarabilirsiniz.', app: 'Ticari sürüme yükselterek mobil uygulamada hizmet bilgilerini ve anormallik izlemeyi görüntüleyebilirsiniz.', cluster: 'Ticari sürüme yükselterek MySQL/Postgres/Redis birincil-kopya kümelerini yönetebilirsiniz.', - vllm: 'Ticari sürüme yükselterek vLLM hizmetlerini 1Panel içinde merkezi olarak dağıtabilir, yapılandırabilir, yaşam döngüsünü yönetebilir ve görevleri izleyebilirsiniz.', + vllm: 'vLLM dağıtımı, hizmet izleme, günlük görüntüleme ve günlük işlemleri tek yerden yönetmek için ticari sürüme yükseltin.', 'model-downloader': - 'Ticari sürüme yükselterek HuggingFace, ModelScope veya uyumlu aynalardan yerel model dosyalarını arayabilir, indirebilir ve yönetebilir; model dizinlerini ve indirme görevlerini merkezi olarak takip edebilirsiniz.', + 'Öne çıkan modelleri incelemek, indirmeleri yönetmek, vLLM Recipes içeriklerini yeniden kullanmak ve indirme kaynakları ile erişim bilgilerini yapılandırmak için ticari sürüme yükseltin.', }, clean: { scan: 'Taramayı başlat', @@ -7218,30 +7218,27 @@ const message = { clusterTitle3: 'Çoğaltma Durumu', clusterContent3: 'Ana-çalışan çoğaltma durumunu ve gecikme bilgilerini görüntüleyerek senkronizasyon sorunlarını gidermeye yardımcı olur', - modelDownloaderTitle1: 'Model Keşfi', + modelDownloaderTitle1: 'Öne Çıkan Modeller', modelDownloaderContent1: - 'HuggingFace model depolarını doğrudan arayın; indirme sayısı, beğeni, model ayrıntıları ve dosya listesini görüntüleyin.', - modelDownloaderTitle2: 'Hızlandırılmış İndirme', + 'Öne çıkan modelleri inceleyin, aile ve ağırlık sağlayıcısına göre filtreleyin, model ayrıntılarını görüntüleyin ve indirmeyi başlatın.', + modelDownloaderTitle2: 'Model İndirme', modelDownloaderContent2: - 'Resmi site, aynalar ve özel modeller için HuggingFace hızlandırma endpointi ve HuggingFace Token yapılandırın.', - modelDownloaderTitle3: 'Görev Takibi', + 'Hugging Face ve ModelScope üzerinden model indirin, ilerlemeyi izleyin, görevleri iptal edin veya yeniden deneyin ve yerel dosyaları yönetin.', + modelDownloaderTitle3: 'vLLM Recipes', modelDownloaderContent3: - 'İndirme kuyruğunu, görev durumunu, ilerlemeyi ve hataları görüntüleyin; iptal, yeniden deneme ve kayıt temizlemeyi destekler.', - modelDownloaderTitle4: 'Yerel Yönetim', + 'Resmî Recipes içeriklerini inceleyip eşitleyin, özel tarifleri yönetin ve modellere uygun dağıtım parametrelerini yeniden kullanın.', + modelDownloaderTitle4: 'Yapılandırma', modelDownloaderContent4: - 'Yerel model dizinlerini tek yerden yönetin; yolları, boyutları, zamanları görüntüleyin ve kullanılmayan model dosyalarını silin.', - vllmTitle1: 'Merkezi Yönetim', + 'Model indirme dizinini, kaynak adreslerini, erişim belirteçlerini ve öne çıkan modellerin otomatik eşitlenmesini yapılandırın.', + vllmTitle1: 'Model Dağıtımı', vllmContent1: - "Dağıtık vLLM örneklerini 1Panel'e entegre edin ve sürümleri, çalışma durumlarını ve çalışma dizinlerini merkezi olarak yönetin.", - vllmTitle2: 'Hızlı Dağıtım', + 'Model dizinini ve konteyner imajını seçin, GPU, port ve başlatma parametrelerini yapılandırın ve vLLM örnekleri oluşturun.', + vllmTitle2: 'Hizmet İzleme', vllmContent2: - 'Görsel formlar aracılığıyla sürüm seçimi, model dizini bağlama ve başlatma parametrelerinin yapılandırılmasını tamamlayarak manuel yapılandırma karmaşıklığını azaltın.', - vllmTitle3: 'Operasyonel Verimlilik', + 'Model hizmetinin performansını izlemek için örnek durumunu, istek sayısını, işlem hacmini ve gecikmeyi görüntüleyin.', + vllmTitle3: 'Günlük İşlemler', vllmContent3: - 'Örnek oluşturma, düzenleme, başlatma, durdurma, yeniden başlatma, silme ve görev takibini tek bir arayüzden gerçekleştirerek yerel model hizmetlerinin yönetim verimliliğini artırın.', - vllmTitle4: 'Esnek Yapılandırma', - vllmContent4: - 'Gelişmiş ayarlar aracılığıyla portları, konteynerleri, başlatma komutlarını ve Compose yapılandırmalarını merkezi olarak yönetin; standartlaştırılmış dağıtım ve sonraki ayarlamaları kolaylaştırın.', + 'Örnekleri başlatın, durdurun, yeniden başlatın ve silin; günlükleri görüntüleyin, terminalleri açın ve işlem görevlerini takip edin.', }, node: { master: 'Ana Düğüm', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 0b1b999a1..ba7488d1e 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -3755,9 +3755,8 @@ const message = { fileExchange: '升級商業版可以在多台伺服器之間快速傳輸檔案。', app: '升級商業版可透過手機APP,檢視服務資訊、異常監控等。', cluster: '升級商業版可以管理 MySQL/Postgres/Redis 主從叢集。', - vllm: '升級商業版可將 vLLM 服務統一納入 1Panel 管理,集中完成部署、設定調整、狀態維運與任務追蹤,降低本地模型服務維護成本。', - 'model-downloader': - '升級商業版可從 HuggingFace、ModelScope 或相容鏡像站搜尋、下載並管理本機模型檔案,統一維護本機模型目錄與下載任務。', + vllm: '升級商業版可部署 vLLM 執行個體、監控服務執行情況,並集中完成日誌查看與日常維運。', + 'model-downloader': '升級商業版可瀏覽精選模型、管理模型下載、重用 vLLM Recipes,並統一設定下載來源與存取憑證。', }, clean: { scan: '開始掃描', @@ -6588,23 +6587,21 @@ const message = { clusterContent2: '統一頁面集中管理多個主從節點,檢視其角色、執行狀態等資訊', clusterTitle3: '複製狀態', clusterContent3: '展示主從複製狀態與延遲資訊,輔助排查同步異常問題', - vllmTitle1: '統一納管', - vllmContent1: '將分散的 vLLM 服務統一接入 1Panel,集中檢視實例、版本、狀態與執行目錄。', - vllmTitle2: '快速部署', - vllmContent2: '透過視覺化表單完成版本選擇、模型目錄掛載與啟動參數設定,減少手動編排成本。', - vllmTitle3: '維運便捷', - vllmContent3: '在同一頁面完成建立、編輯、啟停、重啟、刪除與任務追蹤,提升本地模型服務維護效率。', - vllmTitle4: '設定可控', - vllmContent4: '結合進階設定統一管理連接埠、容器、啟動命令與 Compose 設定,便於標準化交付與後續調整。', - modelDownloaderTitle1: '模型發現', - modelDownloaderContent1: '直接搜尋 HuggingFace 模型倉庫,檢視下載量、點讚數、模型詳情與檔案列表。', - modelDownloaderTitle2: '加速下載', + vllmTitle1: '模型部署', + vllmContent1: '選擇模型目錄與執行映像,設定 GPU、連接埠和啟動參數,快速建立 vLLM 執行個體。', + vllmTitle2: '執行監控', + vllmContent2: '查看執行個體狀態、請求量、吞吐量和延遲,掌握模型服務執行情況。', + vllmTitle3: '日常維運', + vllmContent3: '啟停、重新啟動和刪除執行個體,查看日誌、進入終端並追蹤操作任務。', + modelDownloaderTitle1: '精選模型', + modelDownloaderContent1: '瀏覽精選模型,依模型系列和權重提供方篩選,查看模型資訊並開始下載。', + modelDownloaderTitle2: '模型下載', modelDownloaderContent2: - '支援設定 HuggingFace 加速地址與 HuggingFace Token,適配官方站、鏡像站和私有模型存取。', - modelDownloaderTitle3: '任務追蹤', - modelDownloaderContent3: '統一檢視下載佇列、任務狀態、進度、錯誤資訊,並支援取消、重試和記錄清理。', - modelDownloaderTitle4: '本地管理', - modelDownloaderContent4: '集中維護本地模型目錄,檢視模型路徑、大小和時間,支援刪除不再需要的模型檔案。', + '從 Hugging Face 和 ModelScope 下載模型,查看任務進度,支援取消、重試和本機檔案管理。', + modelDownloaderTitle3: 'vLLM Recipes', + modelDownloaderContent3: '瀏覽及同步官方 Recipes,管理自訂配方,匹配模型並重用部署參數。', + modelDownloaderTitle4: '設定', + modelDownloaderContent4: '統一設定模型下載目錄、下載來源網址、存取 Token 和精選模型自動同步。', }, node: { master: '主節點', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 9f3f0d6c3..26c22e080 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -3785,9 +3785,8 @@ const message = { fileExchange: '升级商业版可以在多台服务器之间快速传输文件。', app: '升级商业版可通过手机 APP,查看服务信息、异常监控等。', cluster: '升级商业版可以管理 MySQL/Postgres/Redis 主从集群。', - vllm: '升级商业版可将 vLLM 服务统一纳入 1Panel 管理,集中完成部署、配置调整、状态运维与任务追踪,降低本地模型服务维护成本。', - 'model-downloader': - '升级商业版可从 HuggingFace、ModelScope 或兼容镜像站搜索、下载并管理本地模型文件,统一维护本地模型目录与下载任务。', + vllm: '升级商业版可部署 vLLM 实例、监控服务运行情况,并集中完成日志查看与日常运维。', + 'model-downloader': '升级商业版可浏览精选模型、管理模型下载、复用 vLLM Recipes,并统一配置下载源与访问凭据。', }, clean: { scan: '开始扫描', @@ -5546,23 +5545,21 @@ const message = { clusterContent2: '统一页面集中管理多个主从节点,查看其角色、运行状态等信息', clusterTitle3: '复制状态', clusterContent3: '展示主从复制状态与延迟信息,辅助排查同步异常问题', - vllmTitle1: '统一纳管', - vllmContent1: '将分散的 vLLM 服务统一接入 1Panel,集中查看实例、版本、状态与运行目录。', - vllmTitle2: '快速部署', - vllmContent2: '通过可视化表单完成版本选择、模型目录挂载与启动参数配置,减少手工编排成本。', - vllmTitle3: '运维便捷', - vllmContent3: '在同一页面完成创建、编辑、启停、重启、删除与任务追踪,提升本地模型服务维护效率。', - vllmTitle4: '配置可控', - vllmContent4: '结合高级设置统一管理端口、容器、启动命令与 Compose 配置,便于标准化交付与后续调整。', - modelDownloaderTitle1: '模型发现', - modelDownloaderContent1: '直接搜索 HuggingFace 模型仓库,查看下载量、点赞数、模型详情与文件列表。', - modelDownloaderTitle2: '加速下载', + vllmTitle1: '模型部署', + vllmContent1: '选择模型目录与运行镜像,配置 GPU、端口和启动参数,快速创建 vLLM 实例。', + vllmTitle2: '运行监控', + vllmContent2: '查看实例运行状态、请求量、吞吐量和延迟,掌握模型服务运行情况。', + vllmTitle3: '日常运维', + vllmContent3: '启停、重启和删除实例,查看日志、进入终端并跟踪操作任务。', + modelDownloaderTitle1: '精选模型', + modelDownloaderContent1: '浏览精选模型,按模型系列和权重提供方筛选,查看模型信息并发起下载。', + modelDownloaderTitle2: '模型下载', modelDownloaderContent2: - '支持配置 HuggingFace 加速地址与 HuggingFace Token,适配官方站、镜像站和私有模型访问。', - modelDownloaderTitle3: '任务追踪', - modelDownloaderContent3: '统一查看下载队列、任务状态、进度、错误信息,并支持取消、重试和记录清理。', - modelDownloaderTitle4: '本地管理', - modelDownloaderContent4: '集中维护本地模型目录,查看模型路径、大小和时间,支持删除不再需要的模型文件。', + '从 Hugging Face 和 ModelScope 下载模型,查看任务进度,支持取消、重试和本地文件管理。', + modelDownloaderTitle3: 'vLLM Recipes', + modelDownloaderContent3: '浏览和同步官方 Recipes,管理自定义配方,匹配模型并复用部署参数。', + modelDownloaderTitle4: '配置', + modelDownloaderContent4: '统一配置模型下载目录、下载源地址、访问 Token 和精选模型自动同步。', }, node: { master: '主节点', diff --git a/frontend/src/routers/modules/ai.ts b/frontend/src/routers/modules/ai.ts index 35984e082..b99387335 100644 --- a/frontend/src/routers/modules/ai.ts +++ b/frontend/src/routers/modules/ai.ts @@ -6,7 +6,7 @@ const aiRouter = { path: '/ai', name: 'AI-Menu', component: Layout, - redirect: '/ai/model', + redirect: '/ai/model/account', meta: { icon: 'p-jiqiren2', title: 'menu.aiTools', @@ -15,35 +15,13 @@ const aiRouter = { { path: '/ai/model/local/ollama', name: 'OllamaModels', - component: () => import('@/views/ai/model/ollama/index.vue'), + component: () => import('@/views/ai/ollama/index.vue'), meta: { icon: 'p-moxing-menu', title: 'aiTools.model.ollamaManagement', permission: 'ai_model_view', }, }, - { - path: '/ai/model', - name: 'AIModel', - redirect: '/ai/model/files', - meta: { - icon: 'p-moxing-menu', - title: 'aiTools.model.vllmManagement', - activeMenu: '/ai/model', - permission: 'ai_model_view', - }, - }, - { - path: '/ai/model/files', - hidden: true, - name: 'ModelFiles', - component: () => import('@/views/ai/model/index.vue'), - meta: { - title: 'aiTools.model.files', - activeMenu: '/ai/model', - permission: 'ai_model_view', - }, - }, { path: '/ai/model/account', name: 'ModelServices', @@ -55,17 +33,6 @@ const aiRouter = { permission: 'ai_model_view', }, }, - { - path: '/ai/model/local', - hidden: true, - name: 'LocalModel', - component: () => import('@/views/ai/model/index.vue'), - meta: { - title: 'aiTools.model.deployments', - activeMenu: '/ai/model', - permission: 'ai_model_view', - }, - }, { path: '/ai/agents/agent', name: 'Agents', diff --git a/frontend/src/utils/app.ts b/frontend/src/utils/app.ts index 90dfeb5c5..9a5c6407c 100644 --- a/frontend/src/utils/app.ts +++ b/frontend/src/utils/app.ts @@ -39,7 +39,7 @@ export const jumpToInstall = (type: string, key: string) => { }); return true; case 'vllm': - if (globalStore.isProductPro) { + if (globalStore.isProductPro && router.hasRoute('LocalModel')) { router.push({ path: '/ai/model/local', query: { diff --git a/frontend/src/utils/pagination.ts b/frontend/src/utils/pagination.ts new file mode 100644 index 000000000..0cb519bd9 --- /dev/null +++ b/frontend/src/utils/pagination.ts @@ -0,0 +1,13 @@ +import type { ReqPage, ResPage, ResultData } from '@/api/interface'; + +export async function readPages(search: (page: ReqPage) => Promise>>) { + const items: T[] = []; + for (let page = 1; ; page++) { + const res = await search({ page, pageSize: 100 }); + const batch = res.data.items || []; + items.push(...batch); + if (items.length >= res.data.total || batch.length === 0) { + return items; + } + } +} diff --git a/frontend/src/views/ai/agents/agent/add/index.vue b/frontend/src/views/ai/agents/agent/add/index.vue index 7227def47..e9cbc35a7 100644 --- a/frontend/src/views/ai/agents/agent/add/index.vue +++ b/frontend/src/views/ai/agents/agent/add/index.vue @@ -156,7 +156,7 @@ import { } from '@/utils/agent'; import { App } from '@/api/interface/app'; import AdvancedSetting from '@/components/advanced-setting/index.vue'; -import AccountAddDialog from '@/views/ai/model/components/account-form/index.vue'; +import AccountAddDialog from '@/views/ai/model/account-form/index.vue'; import { useGlobalStore } from '@/composables/useGlobalStore'; import i18n from '@/lang'; diff --git a/frontend/src/views/ai/model/components/account-address/index.vue b/frontend/src/views/ai/components/account-address/index.vue similarity index 100% rename from frontend/src/views/ai/model/components/account-address/index.vue rename to frontend/src/views/ai/components/account-address/index.vue diff --git a/frontend/src/views/ai/model/components/connection/index.vue b/frontend/src/views/ai/components/connection/index.vue similarity index 100% rename from frontend/src/views/ai/model/components/connection/index.vue rename to frontend/src/views/ai/components/connection/index.vue diff --git a/frontend/src/views/ai/model/components/gpu-config/index.vue b/frontend/src/views/ai/components/gpu-config/index.vue similarity index 99% rename from frontend/src/views/ai/model/components/gpu-config/index.vue rename to frontend/src/views/ai/components/gpu-config/index.vue index dfbe0f53b..bbdb233c5 100644 --- a/frontend/src/views/ai/model/components/gpu-config/index.vue +++ b/frontend/src/views/ai/components/gpu-config/index.vue @@ -258,7 +258,7 @@ defineExpose({ init, loading }); diff --git a/frontend/src/views/ai/model/components/deployment-manage/index.vue b/frontend/src/views/ai/model/components/deployment-manage/index.vue deleted file mode 100644 index fb5456c62..000000000 --- a/frontend/src/views/ai/model/components/deployment-manage/index.vue +++ /dev/null @@ -1,333 +0,0 @@ - - - - - diff --git a/frontend/src/views/ai/model/components/sync/index.vue b/frontend/src/views/ai/model/components/sync/index.vue deleted file mode 100644 index cecc92d76..000000000 --- a/frontend/src/views/ai/model/components/sync/index.vue +++ /dev/null @@ -1,128 +0,0 @@ - - - diff --git a/frontend/src/views/ai/model/create/index.vue b/frontend/src/views/ai/model/create/index.vue new file mode 100644 index 000000000..74878c9b0 --- /dev/null +++ b/frontend/src/views/ai/model/create/index.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/frontend/src/views/ai/model/deployment.ts b/frontend/src/views/ai/model/deployment.ts deleted file mode 100644 index ac81080bb..000000000 --- a/frontend/src/views/ai/model/deployment.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { ReqPage, ResPage, ResultData } from '@/api/interface'; -import type { AI } from '@/api/interface/ai'; - -export interface Deployment { - taskID?: string; - appInstallId: number; - agentAccountId: number; - path: string; - upgradable: boolean; - id: number; - servedModelName: string; - name: string; - appVersion: string; - port: number; - modelAccountBaseURL: string; - modelAccountBaseURLType: AI.ModelAccountSync['modelAccountBaseURLType']; - command: string; - imageType: string; - containerName: string; - modelDir: string; - status: string; -} - -interface VllmAPI { - listVllmCommandTemplates: () => Promise>>; - pageVllm: (req: ReqPage & { info: string }) => Promise>>; - operateVllm: ( - req: { id: number; operate: string; taskID: string; forceDelete?: boolean } & Partial, - ) => Promise; -} - -const vllmModules = import.meta.glob('@/xpack/api/modules/vllm.ts'); -export const vllmAvailable = Object.keys(vllmModules).length > 0; - -export function formatVllmVersion(version: string) { - return String(version || '') - .trim() - .replace(/^(nvidia|intel|ascend)-/i, ''); -} - -export async function loadVllmAPI() { - return Object.values(vllmModules)[0](); -} - -export async function readPages(search: (page: ReqPage) => Promise>>) { - const items: T[] = []; - for (let page = 1; ; page++) { - const res = await search({ page, pageSize: 100 }); - const batch = res.data.items || []; - items.push(...batch); - if (items.length >= res.data.total || batch.length === 0) { - return items; - } - } -} - -export function matchesModelRecipe(recipeModel: string, modelPath: string, repoID = '') { - const path = modelPath.replace(/\/+$/, ''); - const name = path.split('/').pop() || ''; - const model = recipeModel.trim().replace(/\/+$/, ''); - return Boolean( - model && [repoID || path.split('/').slice(-2).join('/'), path, name, `/models/${name}`].includes(model), - ); -} diff --git a/frontend/src/views/ai/model/index.vue b/frontend/src/views/ai/model/index.vue index ef0b68d4b..da34ca689 100644 --- a/frontend/src/views/ai/model/index.vue +++ b/frontend/src/views/ai/model/index.vue @@ -1,363 +1,190 @@ - - diff --git a/frontend/src/views/ai/model/ollama/add/index.vue b/frontend/src/views/ai/ollama/add/index.vue similarity index 100% rename from frontend/src/views/ai/model/ollama/add/index.vue rename to frontend/src/views/ai/ollama/add/index.vue diff --git a/frontend/src/views/ai/model/ollama/domain/index.vue b/frontend/src/views/ai/ollama/domain/index.vue similarity index 100% rename from frontend/src/views/ai/model/ollama/domain/index.vue rename to frontend/src/views/ai/ollama/domain/index.vue diff --git a/frontend/src/views/ai/model/ollama/index.vue b/frontend/src/views/ai/ollama/index.vue similarity index 100% rename from frontend/src/views/ai/model/ollama/index.vue rename to frontend/src/views/ai/ollama/index.vue diff --git a/frontend/src/views/ai/model/ollama/models/index.vue b/frontend/src/views/ai/ollama/models/index.vue similarity index 98% rename from frontend/src/views/ai/model/ollama/models/index.vue rename to frontend/src/views/ai/ollama/models/index.vue index a7fc17bc2..c425c1a14 100644 --- a/frontend/src/views/ai/model/ollama/models/index.vue +++ b/frontend/src/views/ai/ollama/models/index.vue @@ -151,13 +151,13 @@ diff --git a/frontend/src/views/app-store/installed/detail/index.vue b/frontend/src/views/app-store/installed/detail/index.vue index 17a3e922f..2f8adf14e 100644 --- a/frontend/src/views/app-store/installed/detail/index.vue +++ b/frontend/src/views/app-store/installed/detail/index.vue @@ -235,9 +235,9 @@ diff --git a/frontend/src/views/model-downloader/index.vue b/frontend/src/views/model-downloader/index.vue deleted file mode 100644 index c444792c6..000000000 --- a/frontend/src/views/model-downloader/index.vue +++ /dev/null @@ -1,1349 +0,0 @@ - - - - - diff --git a/frontend/src/views/model-downloader/source-select.vue b/frontend/src/views/model-downloader/source-select.vue deleted file mode 100644 index f5e7d50e1..000000000 --- a/frontend/src/views/model-downloader/source-select.vue +++ /dev/null @@ -1,105 +0,0 @@ - - - - -