From 3e982a7438c6f4cb720198cb48a61baf3f5e6ad5 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Tue, 3 Feb 2026 16:46:07 +0800 Subject: [PATCH] Automated cherry pick of #24194: Fix(llm): some mistakes, use CmpId & InstantModelId insteadof SvrId & ModelId (#24205) * fix(llm): some mistakes * fix(llm): use CmpId instead of SvrId * fix(llm): instant_model use id instead of modelId --------- Co-authored-by: cwz --- pkg/apis/llm/llm.go | 7 +- pkg/apis/llm/sku.go | 2 + pkg/llm/drivers/llm_container/ollama.go | 43 ++++--- pkg/llm/models/instantmodel.go | 14 ++- pkg/llm/models/llm.go | 8 +- pkg/llm/models/llm_base.go | 14 +-- pkg/llm/models/llm_base_pod.go | 4 +- pkg/llm/models/llm_instant_model.go | 27 ++-- pkg/llm/models/llm_instant_model_quota.go | 2 +- pkg/llm/models/llm_instant_model_sync.go | 93 +++++++------- pkg/llm/models/llm_save_instant_model.go | 16 ++- pkg/llm/models/llm_sku.go | 29 +++++ pkg/llm/models/volume.go | 8 +- pkg/llm/tasks/dify/dify_create_task.go | 6 +- pkg/llm/tasks/dify/dify_delete_task.go | 2 +- pkg/llm/tasks/dify/dify_start_task.go | 2 +- pkg/llm/tasks/dify/dify_stop_task.go | 2 +- pkg/llm/tasks/llm/llm_create_task.go | 6 +- pkg/llm/tasks/llm/llm_delete_task.go | 2 +- pkg/llm/tasks/llm/llm_start_task.go | 2 +- pkg/llm/tasks/llm/llm_stop_task.go | 2 +- pkg/llm/tasks/llm/llm_sync_status_task.go | 4 +- pkg/llm/tasks/volume/volume_delete_task.go | 6 +- scripts/download_ollama_models.sh | 137 --------------------- 24 files changed, 177 insertions(+), 261 deletions(-) delete mode 100644 scripts/download_ollama_models.sh diff --git a/pkg/apis/llm/llm.go b/pkg/apis/llm/llm.go index fa133180be..2ef640e6b0 100644 --- a/pkg/apis/llm/llm.go +++ b/pkg/apis/llm/llm.go @@ -48,15 +48,16 @@ type LLMBaseListDetails struct { type MountedModelInfo struct { FullName string `json:"fullname"` // 模型全名,如: qwen3:8b - Id string `json:"id"` // 模型ID,如: 500a1f067a9f + ModelId string `json:"model_id"` // 模型ID,如: 500a1f067a9f + Id string `json:"id"` // 秒装包的 ID 主键 } type LLMListDetails struct { LLMBaseListDetails - LLMSku string + LLMSku string `json:"llm_sku"` - MountedModels []MountedModelInfo + MountedModels []MountedModelInfo `json:"mounted_models"` } type LLMBaseCreateInput struct { diff --git a/pkg/apis/llm/sku.go b/pkg/apis/llm/sku.go index 5ea707b51f..bce1b468f2 100644 --- a/pkg/apis/llm/sku.go +++ b/pkg/apis/llm/sku.go @@ -116,6 +116,8 @@ type LLMSkuDetails struct { ImageLabel string ImageName string + MountedModelDetails []MountedModelInfo `json:"mounted_model_details"` + Template string `json:"template"` } diff --git a/pkg/llm/drivers/llm_container/ollama.go b/pkg/llm/drivers/llm_container/ollama.go index 068e48ad79..53c327924e 100644 --- a/pkg/llm/drivers/llm_container/ollama.go +++ b/pkg/llm/drivers/llm_container/ollama.go @@ -102,9 +102,9 @@ func (o *ollama) GetContainerSpec(ctx context.Context, llm *models.SLLM, image * { Disk: &commonapi.ContainerVolumeMountDisk{ SubDirectory: api.LLM_OLLAMA, - Overlay: &commonapi.ContainerVolumeMountDiskOverlay{ - LowerDir: []string{api.LLM_OLLAMA_HOST_PATH}, - }, + // Overlay: &commonapi.ContainerVolumeMountDiskOverlay{ + // LowerDir: []string{api.LLM_OLLAMA_HOST_PATH}, + // }, PostOverlay: postOverlays, Index: &diskIndex, }, @@ -396,7 +396,6 @@ func (o *ollama) GetProbedInstantModelsExt(ctx context.Context, userCred mcclien return nil, errors.Wrap(err, "get llm container") } - // get all effective models getModels := "ollama list" // NAME ID SIZE MODIFIED modelsOutput, err := exec(ctx, lc.CmpId, getModels, 10) if err != nil { @@ -404,25 +403,35 @@ func (o *ollama) GetProbedInstantModelsExt(ctx context.Context, userCred mcclien } lines := strings.Split(strings.TrimSpace(modelsOutput), "\n") - models := make(map[string]api.LLMInternalInstantMdlInfo, len(lines)-1) + modelsMap := make(map[string]api.LLMInternalInstantMdlInfo, len(lines)-1) for i := 1; i < len(lines); i++ { fields := strings.Fields(lines[i]) - if len(fields) > 2 { - if len(mdlIds) > 0 && !utils.IsInStringArray(fields[1], mdlIds) { + if len(fields) < 3 { + continue + } + ollamaId := fields[1] + modelName, modelTag, _ := llm.GetLargeLanguageModelName(fields[0]) + instMdl, _ := models.GetInstantModelManager().FindInstantModel(ollamaId, modelTag, true) + var key string + if instMdl != nil { + key = instMdl.Id + if len(mdlIds) > 0 && !utils.IsInStringArray(key, mdlIds) { continue } - modelName, modelTag, _ := llm.GetLargeLanguageModelName(fields[0]) - models[fields[1]] = api.LLMInternalInstantMdlInfo{ - Name: modelName, - Tag: modelTag, - ModelId: fields[1], - // Modified: fields[3], + } else { + key = ollamaId + if len(mdlIds) > 0 { + continue } } + modelsMap[key] = api.LLMInternalInstantMdlInfo{ + Name: modelName, + Tag: modelTag, + ModelId: ollamaId, + } } - // for each model, get manifests file, find blobs, calculate size - for modelId, model := range models { + for key, model := range modelsMap { manifests, err := getManifests(ctx, lc.CmpId, model.Name, model.Tag) if err != nil { return nil, errors.Wrap(err, "get manifests") @@ -433,10 +442,10 @@ func (o *ollama) GetProbedInstantModelsExt(ctx context.Context, userCred mcclien model.Size += layer.Size model.Blobs = append(model.Blobs, layer.Digest) } - models[modelId] = model + modelsMap[key] = model } - return models, nil + return modelsMap, nil } func (o *ollama) ValidateMounts(mounts []string, mdlName string, mdlTag string) ([]string, error) { diff --git a/pkg/llm/models/instantmodel.go b/pkg/llm/models/instantmodel.go index 0a1ce16c71..558a250a57 100644 --- a/pkg/llm/models/instantmodel.go +++ b/pkg/llm/models/instantmodel.go @@ -271,10 +271,11 @@ func (man *SInstantModelManager) FetchCustomizeColumns( LlmId: llmInstModel.LlmId, LlmName: llm.Name, } - if _, ok := modelMountedByMap[llmInstModel.ModelId]; !ok { - modelMountedByMap[llmInstModel.ModelId] = make([]apis.MountedByLLMInfo, 0) + instantModelId := llmInstModel.InstantModelId + if _, ok := modelMountedByMap[instantModelId]; !ok { + modelMountedByMap[instantModelId] = make([]apis.MountedByLLMInfo, 0) } - modelMountedByMap[llmInstModel.ModelId] = append(modelMountedByMap[llmInstModel.ModelId], info) + modelMountedByMap[instantModelId] = append(modelMountedByMap[instantModelId], info) } for i := range res { @@ -286,7 +287,7 @@ func (man *SInstantModelManager) FetchCustomizeColumns( res[i].CacheCount = status.CacheCount res[i].CachedCount = status.CachedCount } - if mountedBy, ok := modelMountedByMap[instModel.ModelId]; ok { + if mountedBy, ok := modelMountedByMap[instModel.Id]; ok { res[i].MountedByLLMs = mountedBy } @@ -530,7 +531,7 @@ func (man *SInstantModelManager) GetInstantModelById(id string) (*SInstantModel, return obj.(*SInstantModel), nil } -func (man *SInstantModelManager) findInstantModel(mdlId, tag string, isEnabled bool) (*SInstantModel, error) { +func (man *SInstantModelManager) FindInstantModel(mdlId, tag string, isEnabled bool) (*SInstantModel, error) { q := man.Query().Equals("model_id", mdlId).Equals("status", imageapi.IMAGE_STATUS_ACTIVE) if isEnabled { q = q.IsTrue("enabled") @@ -667,7 +668,8 @@ func (model *SInstantModel) PerformPrivate( func (model *SInstantModel) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error { if model.Enabled.IsTrue() { for _, man := range []MountedModelModelManager{GetLLMSkuManager(), GetVolumeManager()} { - used, err := man.IsPremountedModelName(model.ModelName + ":" + model.ModelTag + "-" + model.ModelId) + // volume/sku 存储格式为 modelFullName-instantModelId + used, err := man.IsPremountedModelName(model.ModelName + ":" + model.ModelTag + "-" + model.Id) if err != nil { return errors.Wrap(err, "IsPremountedModelName") } diff --git a/pkg/llm/models/llm.go b/pkg/llm/models/llm.go index c0d891c802..c17c387851 100644 --- a/pkg/llm/models/llm.go +++ b/pkg/llm/models/llm.go @@ -145,8 +145,8 @@ func (man *SLLMManager) FetchCustomizeColumns( ids[idx] = llm.Id skuIds[idx] = llm.LLMSkuId imgIds[idx] = llm.LLMImageId - if !utils.IsInArray(llm.SvrId, serverIds) { - serverIds = append(serverIds, llm.SvrId) + if !utils.IsInArray(llm.CmpId, serverIds) { + serverIds = append(serverIds, llm.CmpId) } if len(llm.NetworkId) > 0 { networkIds = append(networkIds, llm.NetworkId) @@ -165,7 +165,7 @@ func (man *SLLMManager) FetchCustomizeColumns( for i, id := range ids { if id == volume.LLMId { res[i].Volume = api.Volume{ - Id: volume.Id, + Id: volume.CmpId, Name: volume.Name, TemplateId: volume.TemplateId, StorageType: volume.StorageType, @@ -262,7 +262,7 @@ func (man *SLLMManager) FetchCustomizeColumns( for i := range llms { llmStatus := api.LLM_STATUS_UNKNOWN llm := llms[i] - if guest, ok := serverMap[llm.SvrId]; ok { + if guest, ok := serverMap[llm.CmpId]; ok { // find guest if len(guest.Containers) == 0 { llmStatus = api.LLM_LLM_STATUS_NO_CONTAINER diff --git a/pkg/llm/models/llm_base.go b/pkg/llm/models/llm_base.go index d2b224b1ea..c536c33b97 100644 --- a/pkg/llm/models/llm_base.go +++ b/pkg/llm/models/llm_base.go @@ -44,7 +44,7 @@ type SLLMBase struct { db.SVirtualResourceBase db.SEnabledResourceBase - SvrId string `width:"128" charset:"ascii" nullable:"true" list:"user"` + CmpId string `width:"128" charset:"ascii" nullable:"true" list:"user"` LLMIp string `width:"20" charset:"ascii" nullable:"true" list:"user"` // Hypervisor string `width:"128" charset:"ascii" nullable:"true" list:"user"` Priority int `nullable:"false" default:"100" list:"user"` @@ -298,7 +298,7 @@ func (man *SLLMBaseManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQue } func (llm *SLLMBase) GetServer(ctx context.Context) (*computeapi.ServerDetails, error) { - return cloudutil.GetServer(ctx, llm.SvrId) + return cloudutil.GetServer(ctx, llm.CmpId) } func (llm *SLLMBase) GetVolume() (*SVolume, error) { @@ -353,7 +353,7 @@ func (llm *SLLMBase) RealDelete(ctx context.Context, userCred mcclient.TokenCred } func (llm *SLLMBase) ServerDelete(ctx context.Context, userCred mcclient.TokenCredential, s *mcclient.ClientSession) error { - if len(llm.SvrId) == 0 { + if len(llm.CmpId) == 0 { return nil } server, err := llm.GetServer(ctx) @@ -367,12 +367,12 @@ func (llm *SLLMBase) ServerDelete(ctx context.Context, userCred mcclient.TokenCr if server.DisableDelete != nil && *server.DisableDelete { // update to allow delete s2 := auth.GetSession(ctx, userCred, "") - _, err = compute.Servers.Update(s2, llm.SvrId, jsonutils.Marshal(map[string]interface{}{"disable_delete": false})) + _, err = compute.Servers.Update(s2, llm.CmpId, jsonutils.Marshal(map[string]interface{}{"disable_delete": false})) if err != nil { return errors.Wrap(err, "update server to delete") } } - _, err = compute.Servers.DeleteWithParam(s, llm.SvrId, jsonutils.Marshal(map[string]interface{}{ + _, err = compute.Servers.DeleteWithParam(s, llm.CmpId, jsonutils.Marshal(map[string]interface{}{ "override_pending_delete": true, }), nil) if err != nil { @@ -382,7 +382,7 @@ func (llm *SLLMBase) ServerDelete(ctx context.Context, userCred mcclient.TokenCr } func (llm *SLLMBase) WaitDelete(ctx context.Context, userCred mcclient.TokenCredential, timeoutSecs int) error { - return cloudutil.WaitDelete[computeapi.ServerDetails](ctx, &compute.Servers, llm.SvrId, timeoutSecs) + return cloudutil.WaitDelete[computeapi.ServerDetails](ctx, &compute.Servers, llm.CmpId, timeoutSecs) } func (llm *SLLMBase) getImage(imageId string) (*SLLMImage, error) { @@ -394,5 +394,5 @@ func (llm *SLLMBase) getImage(imageId string) (*SLLMImage, error) { } func (llm *SLLMBase) WaitServerStatus(ctx context.Context, userCred mcclient.TokenCredential, targetStatus []string, timeoutSecs int) (*computeapi.ServerDetails, error) { - return cloudutil.WaitServerStatus(ctx, llm.SvrId, targetStatus, timeoutSecs) + return cloudutil.WaitServerStatus(ctx, llm.CmpId, targetStatus, timeoutSecs) } diff --git a/pkg/llm/models/llm_base_pod.go b/pkg/llm/models/llm_base_pod.go index 8fd83eb8dc..b316d91046 100644 --- a/pkg/llm/models/llm_base_pod.go +++ b/pkg/llm/models/llm_base_pod.go @@ -5,7 +5,6 @@ import ( "fmt" "yunion.io/x/jsonutils" - "yunion.io/x/pkg/util/seclib" "yunion.io/x/onecloud/pkg/apis" computeapi "yunion.io/x/onecloud/pkg/apis/compute" @@ -45,7 +44,8 @@ func GetLLMBasePodCreateInput( data.VcpuCount = skuBase.Cpu data.VmemSize = skuBase.Memory + 1 - data.Name = input.Name + "-" + seclib.RandomPassword(6) + // data.Name = input.Name + "-" + seclib.RandomPassword(6) + data.Name = input.Name // disks data.Disks = make([]*computeapi.DiskConfig, 0) diff --git a/pkg/llm/models/llm_instant_model.go b/pkg/llm/models/llm_instant_model.go index 1a4caedc53..34f793cc34 100644 --- a/pkg/llm/models/llm_instant_model.go +++ b/pkg/llm/models/llm_instant_model.go @@ -44,10 +44,9 @@ type SLLMInstantModel struct { db.SResourceBase db.SStatusResourceBase - // InstantModelId string `name:"model_id" width:"128" charset:"ascii" nullable:"false" list:"user" primary:"true"` LlmId string `width:"128" charset:"ascii" nullable:"false" list:"user" primary:"true"` - // Model ID, large language model's ID, referring to special model, such as qwen3:8b - ModelId string `name:"model_id" width:"128" charset:"ascii" nullable:"false" list:"user" primary:"true"` + // InstantModelId instant model 主键 id(SInstantModel.Id) + InstantModelId string `name:"model_id" width:"128" charset:"ascii" nullable:"false" list:"user" primary:"true"` // Model Tag Tag string `width:"64" charset:"utf8" nullable:"true" list:"user"` @@ -58,8 +57,8 @@ type SLLMInstantModel struct { // IsSystem tristate.TriState `list:"user"` } -func (man *SLLMInstantModelManager) fetchLLMInstantModel(llmId string, mdlId string) (*SLLMInstantModel, error) { - q := man.RawQuery().Equals("llm_id", llmId).Equals("model_id", mdlId) +func (man *SLLMInstantModelManager) fetchLLMInstantModel(llmId string, instantModelId string) (*SLLMInstantModel, error) { + q := man.RawQuery().Equals("llm_id", llmId).Equals("model_id", instantModelId) llmInstantModel := SLLMInstantModel{} err := q.First(&llmInstantModel) if err != nil { @@ -79,15 +78,15 @@ func (man *SLLMInstantModelManager) getDeletedModelIds(llmId string) ([]string, if err != nil { return nil, errors.Wrap(err, "Query") } - modelIds := make([]string, len(llmInstantModel)) + instantModelIds := make([]string, len(llmInstantModel)) for i := range llmInstantModel { - modelIds[i] = llmInstantModel[i].ModelId + instantModelIds[i] = llmInstantModel[i].InstantModelId } - return modelIds, nil + return instantModelIds, nil } -func (man *SLLMInstantModelManager) updateInstantModel(ctx context.Context, llmId string, mdlId string, mdlName string, tag string, probed, mounted *bool) (*SLLMInstantModel, error) { - mdl, err := man.fetchLLMInstantModel(llmId, mdlId) +func (man *SLLMInstantModelManager) updateInstantModel(ctx context.Context, llmId string, instantModelId string, mdlName string, tag string, probed, mounted *bool) (*SLLMInstantModel, error) { + mdl, err := man.fetchLLMInstantModel(llmId, instantModelId) if err != nil && errors.Cause(err) != errors.ErrNotFound { return nil, errors.Wrap(err, "updateInstantModel") } @@ -105,9 +104,9 @@ func (man *SLLMInstantModelManager) updateInstantModel(ctx context.Context, llmI if mdl == nil { // if no such app mdl = &SLLMInstantModel{ - LlmId: llmId, - ModelId: mdlId, - ModelName: mdlName, + LlmId: llmId, + InstantModelId: instantModelId, + ModelName: mdlName, // IsSystem: tristate.None, // Entry: entry, } @@ -200,7 +199,7 @@ func (man *SLLMInstantModelManager) filterModels(q *sqlchemy.SQuery, isProbed, i } func (mdl *SLLMInstantModel) FindInstantModel(isInstall bool) (*SInstantModel, error) { - instMdl, err := GetInstantModelManager().findInstantModel(mdl.ModelId, mdl.Tag, isInstall) + instMdl, err := GetInstantModelManager().GetInstantModelById(mdl.InstantModelId) if err != nil { return nil, errors.Wrap(err, "FindInstantModel") } diff --git a/pkg/llm/models/llm_instant_model_quota.go b/pkg/llm/models/llm_instant_model_quota.go index f6e2aeb769..0d12c61eb3 100644 --- a/pkg/llm/models/llm_instant_model_quota.go +++ b/pkg/llm/models/llm_instant_model_quota.go @@ -20,7 +20,7 @@ func (llm *SLLM) GetInstantModelSizeGb() float64 { } totalSizeGb := 0.0 for _, model := range models { - instModel, _ := GetInstantModelManager().findInstantModel(model.ModelId, model.Tag, false) + instModel, _ := GetInstantModelManager().GetInstantModelById(model.InstantModelId) if instModel == nil { continue } diff --git a/pkg/llm/models/llm_instant_model_sync.go b/pkg/llm/models/llm_instant_model_sync.go index effb6e6636..ff12a5314a 100644 --- a/pkg/llm/models/llm_instant_model_sync.go +++ b/pkg/llm/models/llm_instant_model_sync.go @@ -90,27 +90,26 @@ func (llm *SLLM) getProbedMountedInstantModels(ctx context.Context, userCred mcc if err != nil { return nil, errors.Wrap(err, "llm.getMountedInstantModels") } - for mdlId := range mounted { - if _, ok := mdlMap[mdlId]; ok { - mdlMap[mdlId].Mounted = true + for instantModelId := range mounted { + if _, ok := mdlMap[instantModelId]; ok { + mdlMap[instantModelId].Mounted = true } else { - mdlMap[mdlId] = &sInstantModelStatus{ - LLMInternalInstantMdlInfo: apis.LLMInternalInstantMdlInfo{ - ModelId: mdlId, - }, - Mounted: true, + // 仅 mounted 未 probed 时无 ollama model id,ModelId 留空 + mdlMap[instantModelId] = &sInstantModelStatus{ + LLMInternalInstantMdlInfo: apis.LLMInternalInstantMdlInfo{}, + Mounted: true, } } } return mdlMap, nil } -func (llm *SLLM) uninstallInstantModel(ctx context.Context, userCred mcclient.TokenCredential, mdlId string) error { +func (llm *SLLM) uninstallInstantModel(ctx context.Context, userCred mcclient.TokenCredential, instantModelId string) error { boolFalse := false // uninstalled probed := &boolFalse mounted := &boolFalse - _, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mdlId, "", "", probed, mounted) + _, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, instantModelId, "", "", probed, mounted) if err != nil { return errors.Wrap(err, "uninstallPackage") } @@ -119,7 +118,7 @@ func (llm *SLLM) uninstallInstantModel(ctx context.Context, userCred mcclient.To func findInstantModelWithModelInfo(allModels []SLLMInstantModel, mdl apis.ModelInfo) *SLLMInstantModel { for i := range allModels { - if allModels[i].ModelId == mdl.ModelId { + if allModels[i].InstantModelId == mdl.Id { return &allModels[i] } } @@ -157,7 +156,7 @@ func isImageInUnmountModels(imageId string, mdls []SLLMInstantModel) (bool, erro return false, nil } for i := range mdls { - if mdls[i].ModelId == instMdl.ModelId { + if mdls[i].InstantModelId == instMdl.Id { return true, nil } } @@ -188,15 +187,15 @@ func (llm *SLLM) RefreshInstantModels(ctx context.Context, userCred mcclient.Tok mdl := models[i] var probed *bool var mounted *bool - if status, ok := mdlMap[mdl.ModelId]; ok { + if status, ok := mdlMap[mdl.InstantModelId]; ok { // probed probed = &status.Probed mounted = &status.Mounted - _, err = GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mdl.ModelId, status.Name, status.Tag, probed, mounted) - delete(mdlMap, mdl.ModelId) + _, err = GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mdl.InstantModelId, status.Name, status.Tag, probed, mounted) + delete(mdlMap, mdl.InstantModelId) } else { // uninstalled - err = llm.uninstallInstantModel(ctx, userCred, mdl.ModelId) + err = llm.uninstallInstantModel(ctx, userCred, mdl.InstantModelId) } if err != nil { errs = append(errs, err) @@ -204,8 +203,8 @@ func (llm *SLLM) RefreshInstantModels(ctx context.Context, userCred mcclient.Tok } if len(mdlMap) > 0 { - for mdlId, status := range mdlMap { - _, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mdlId, status.Name, status.Tag, &status.Probed, &status.Mounted) + for instantModelId, status := range mdlMap { + _, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, instantModelId, status.Name, status.Tag, &status.Probed, &status.Mounted) if err != nil { errs = append(errs, err) } @@ -260,7 +259,7 @@ func (llm *SLLM) PerformQuickModels(ctx context.Context, userCred mcclient.Token } } } else { - mdl, err := GetInstantModelManager().findInstantModel(input.Models[i].ModelId, input.Models[i].Tag, true) + mdl, err := GetInstantModelManager().FindInstantModel(input.Models[i].ModelId, input.Models[i].Tag, true) if err != nil { return nil, errors.Wrapf(err, "findInstantModel %s %s", input.Models[i].ModelId, input.Models[i].Tag) } @@ -274,7 +273,7 @@ func (llm *SLLM) PerformQuickModels(ctx context.Context, userCred mcclient.Token } } if !apis.IsLLMContainerType(input.Models[i].LlmType) || apis.LLMContainerType(input.Models[i].LlmType) != llm.GetLLMContainerDriver().GetType() { - errs = append(errs, errors.Wrapf(httperrors.ErrInvalidStatus, "model %s is not of type %s", input.Models[i].ModelId, llm.GetLLMContainerDriver().GetType())) + errs = append(errs, errors.Wrapf(httperrors.ErrInvalidStatus, "model %s is not of type %s", input.Models[i].Id, llm.GetLLMContainerDriver().GetType())) } } if len(errs) > 0 { @@ -348,7 +347,7 @@ func (llm *SLLM) FetchModelsFullName(isProbed, isMounted *bool) ([]string, error } mdlFullNames := make([]string, len(models)) for idx, mdl := range models { - mdlFullNames[idx] = mdl.ModelName + ":" + mdl.Tag + "-" + mdl.ModelId + mdlFullNames[idx] = mdl.ModelName + ":" + mdl.Tag + "-" + mdl.InstantModelId } return mdlFullNames, nil } @@ -366,9 +365,15 @@ func (llm *SLLM) FetchMountedModelInfo() ([]apis.MountedModelInfo, error) { } result := make([]apis.MountedModelInfo, len(models)) for idx, mdl := range models { + instMdl, _ := GetInstantModelManager().GetInstantModelById(mdl.InstantModelId) + modelIdForDisplay := "" + if instMdl != nil { + modelIdForDisplay = instMdl.ModelId + } result[idx] = apis.MountedModelInfo{ FullName: mdl.ModelName + ":" + mdl.Tag, - Id: mdl.ModelId, + ModelId: modelIdForDisplay, + Id: mdl.InstantModelId, } } return result, nil @@ -428,7 +433,7 @@ func (llm *SLLM) RequestUnmountModel(ctx context.Context, userCred mcclient.Toke var modelIds []string for i := range unmountModels { - modelIds = append(modelIds, unmountModels[i].ModelId) + modelIds = append(modelIds, unmountModels[i].InstantModelId) } return modelIds, unmountOverlays, nil } @@ -459,7 +464,7 @@ func (llm *SLLM) RequestMountModels(ctx context.Context, userCred mcclient.Token log.Errorf("preinstallPackage fail %s", err) } } - mdlIds = append(mdlIds, model.ModelId) + mdlIds = append(mdlIds, model.InstantModelId) } targetDirs := make([]string, 0) for i := range overlays { @@ -610,9 +615,9 @@ func (llm *SLLM) UpdateVolumeMountedModelFullNames(mdlFullNames []string) error } type mdlFullNameInfo struct { - ModelId string - ModelFullName string - IsMounted bool + InstantModelId string // instant model 主键 id + ModelFullName string + IsMounted bool } func (llm *SLLM) UpdateMountedModelFullNames(ctx context.Context, userCred mcclient.TokenCredential, mdlinfos []string, isReset bool, imageId string, skuId string) error { @@ -620,9 +625,9 @@ func (llm *SLLM) UpdateMountedModelFullNames(ctx context.Context, userCred mccli for i := range mdlinfos { parts := strings.Split(mdlinfos[i], "@") mdlFullNameInfos[parts[0]] = &mdlFullNameInfo{ - ModelId: parts[0], - ModelFullName: parts[1], - IsMounted: false, + InstantModelId: parts[0], + ModelFullName: parts[1], + IsMounted: false, } } @@ -646,15 +651,15 @@ func (llm *SLLM) UpdateMountedModelFullNames(ctx context.Context, userCred mccli return errors.Wrap(err, "FetchByIdOrName") } instantModle := instMdl.(*SInstantModel) - if !isReset && slices.Contains(deletedModelIds, instantModle.ModelId) { + if !isReset && slices.Contains(deletedModelIds, instantModle.Id) { // if not reset, and the model is deleted, skip it continue } - if _, ok := mdlFullNameInfos[instantModle.ModelId]; !ok { - mdlFullNameInfos[instantModle.ModelId] = &mdlFullNameInfo{ - ModelId: instantModle.ModelId, - ModelFullName: instantModle.ModelName + ":" + instantModle.ModelTag, - IsMounted: false, + if _, ok := mdlFullNameInfos[instantModle.Id]; !ok { + mdlFullNameInfos[instantModle.Id] = &mdlFullNameInfo{ + InstantModelId: instantModle.Id, + ModelFullName: instantModle.ModelName + ":" + instantModle.ModelTag, + IsMounted: false, } } } @@ -668,15 +673,15 @@ func (llm *SLLM) UpdateMountedModelFullNames(ctx context.Context, userCred mccli } for i := range mountedModels { find := false - if _, ok := mdlFullNameInfos[mountedModels[i].ModelId]; ok { + if _, ok := mdlFullNameInfos[mountedModels[i].InstantModelId]; ok { find = true - mdlFullNameInfos[mountedModels[i].ModelId].IsMounted = true + mdlFullNameInfos[mountedModels[i].InstantModelId].IsMounted = true } if isReset && !find { // remove instant model not in mdlInfos mountedModel := mountedModels[i] - log.Debugf("UpdateMountedModelFullNames remove model %s", mountedModel.ModelId) - _, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mountedModel.ModelId, "", "", &boolFalse, &boolFalse) + log.Debugf("UpdateMountedModelFullNames remove model %s", mountedModel.InstantModelId) + _, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mountedModel.InstantModelId, "", "", &boolFalse, &boolFalse) if err != nil { return errors.Wrap(err, "remove instant model") } @@ -685,10 +690,10 @@ func (llm *SLLM) UpdateMountedModelFullNames(ctx context.Context, userCred mccli installModelFullNames := make([]string, 0) for _, mdlFullNameInfo := range mdlFullNameInfos { - installModelFullNames = append(installModelFullNames, fmt.Sprintf("%s-%s", mdlFullNameInfo.ModelFullName, mdlFullNameInfo.ModelId)) + installModelFullNames = append(installModelFullNames, fmt.Sprintf("%s-%s", mdlFullNameInfo.ModelFullName, mdlFullNameInfo.InstantModelId)) if !mdlFullNameInfo.IsMounted { modelName, modelTag, _ := llm.GetLargeLanguageModelName(mdlFullNameInfo.ModelFullName) - _, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mdlFullNameInfo.ModelId, modelName, modelTag, &boolFalse, &boolTrue) + _, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mdlFullNameInfo.InstantModelId, modelName, modelTag, &boolFalse, &boolTrue) if err != nil { return errors.Wrap(err, "install model") } @@ -735,9 +740,9 @@ func (llm *SLLM) getMountingModelsPostOverlay(ctx context.Context, input apis.LL continue } } - model, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mdl.ModelId, mdl.DisplayName, mdl.Tag, nil, nil) + model, err := GetLLMInstantModelManager().updateInstantModel(ctx, llm.Id, mdl.Id, mdl.DisplayName, mdl.Tag, nil, nil) if err != nil { - return nil, nil, errors.Wrapf(err, "updateInstantModel %s", mdl.ModelId) + return nil, nil, errors.Wrapf(err, "updateInstantModel %s", mdl.Id) } models = append(models, *model) } diff --git a/pkg/llm/models/llm_save_instant_model.go b/pkg/llm/models/llm_save_instant_model.go index 1a035fbeab..71664c5043 100644 --- a/pkg/llm/models/llm_save_instant_model.go +++ b/pkg/llm/models/llm_save_instant_model.go @@ -41,17 +41,23 @@ func (llm *SLLM) PerformSaveInstantModel( return nil, httperrors.NewInvalidStatusError("LLM is not running") } - mdlInfos, err := llm.getProbedInstantModelsExt(ctx, userCred, input.ModelId) + mdlInfos, err := llm.getProbedInstantModelsExt(ctx, userCred) if err != nil { return nil, errors.Wrap(err, "getProbedPackagesExt") } - mdlInfo, ok := mdlInfos[input.ModelId] - if !ok { + var mdlInfo *api.LLMInternalInstantMdlInfo + for _, info := range mdlInfos { + if info.ModelId == input.ModelId { + mdlInfo = &info + break + } + } + if mdlInfo == nil { return nil, httperrors.NewBadRequestError("ModelId %s not found", input.ModelId) } - mountDirs, err := llm.detectModelPaths(ctx, userCred, mdlInfo) + mountDirs, err := llm.detectModelPaths(ctx, userCred, *mdlInfo) if err != nil { return nil, errors.Wrap(err, "detectModelPaths") } @@ -144,7 +150,7 @@ func (llm *SLLM) DoSaveModelImage(ctx context.Context, userCred mcclient.TokenCr saveImageInput := computeapi.ContainerSaveVolumeMountToImageInput{ GenerateName: input.ModelFullName, - Notes: fmt.Sprintf("instance model image for %s(%s)", input.ModelId, instantModel.ModelName+":"+instantModel.ModelTag), + Notes: fmt.Sprintf("instance model image for %s(%s)", instantModel.ModelId, instantModel.ModelName+":"+instantModel.ModelTag), Index: 0, Dirs: saveDirs, UsedByPostOverlay: true, diff --git a/pkg/llm/models/llm_sku.go b/pkg/llm/models/llm_sku.go index 8f5f11ee97..4874ad1905 100644 --- a/pkg/llm/models/llm_sku.go +++ b/pkg/llm/models/llm_sku.go @@ -109,6 +109,7 @@ func (manager *SLLMSkuManager) FetchCustomizeColumns( }{} q.All(&details) res := make([]api.LLMSkuDetails, len(objs)) + mountedModelIds := make([]string, 0) for i, sku := range skus { res[i].SharableVirtualResourceDetails = virows[i] for _, v := range details { @@ -117,6 +118,34 @@ func (manager *SLLMSkuManager) FetchCustomizeColumns( break } } + if len(sku.MountedModels) > 0 { + mountedModelIds = append(mountedModelIds, sku.MountedModels...) + } + } + + // fetch mounted models + if len(mountedModelIds) > 0 { + instModels := make(map[string]SInstantModel) + err := db.FetchModelObjectsByIds(GetInstantModelManager(), "id", mountedModelIds, &instModels) + if err != nil { + log.Errorf("FetchModelObjectsByIds InstantModelManager fail %s", err) + } else { + for i, sku := range skus { + if len(sku.MountedModels) > 0 { + res[i].MountedModelDetails = make([]api.MountedModelInfo, 0) + for _, modelId := range sku.MountedModels { + if instModel, ok := instModels[modelId]; ok { + info := api.MountedModelInfo{ + Id: instModel.Id, + ModelId: instModel.ModelId, + FullName: instModel.ModelName + ":" + instModel.ModelTag, + } + res[i].MountedModelDetails = append(res[i].MountedModelDetails, info) + } + } + } + } + } } { images := make(map[string]SLLMImage) diff --git a/pkg/llm/models/volume.go b/pkg/llm/models/volume.go index e559e80413..fc3f367a72 100644 --- a/pkg/llm/models/volume.go +++ b/pkg/llm/models/volume.go @@ -62,7 +62,7 @@ type SVolume struct { TemplateId string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"user"` // size in MB SizeMB int `nullable:"false" default:"0" create:"optional" list:"user" update:"user"` - SvrId string `width:"128" charset:"ascii" nullable:"true" list:"user"` + CmpId string `width:"128" charset:"ascii" nullable:"true" list:"user"` Containers api.ContainerVolumeRelations `charset:"utf8" nullable:"true" list:"user" create:"optional"` } @@ -106,17 +106,17 @@ func (volume *SVolume) UpdateMountedModelFullNames(mountModels []string) error { } func (volume *SVolume) GetDisk(ctx context.Context) (*computeapi.DiskDetails, error) { - if len(volume.SvrId) == 0 { + if len(volume.CmpId) == 0 { return nil, errors.ErrInvalidStatus } s := auth.GetAdminSession(ctx, "") disk := computeapi.DiskDetails{} - resp, err := compute.Disks.GetById(s, volume.SvrId, jsonutils.Marshal(map[string]interface{}{ + resp, err := compute.Disks.GetById(s, volume.CmpId, jsonutils.Marshal(map[string]interface{}{ "scope": "max", })) if err != nil { if httputils.ErrorCode(err) == 404 { - return nil, errors.Wrapf(errors.ErrNotFound, "GetById %s", volume.SvrId) + return nil, errors.Wrapf(errors.ErrNotFound, "GetById %s", volume.CmpId) } return nil, errors.Wrap(err, "fetch disk") } diff --git a/pkg/llm/tasks/dify/dify_create_task.go b/pkg/llm/tasks/dify/dify_create_task.go index a16da4845f..ec17969157 100644 --- a/pkg/llm/tasks/dify/dify_create_task.go +++ b/pkg/llm/tasks/dify/dify_create_task.go @@ -56,10 +56,10 @@ func (task *DifyCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, } db.Update(dify, func() error { - dify.SvrId = serverId + dify.CmpId = serverId return nil }) - dify.SvrId = serverId + dify.CmpId = serverId return nil }) // var expectStatus []string @@ -91,7 +91,7 @@ func (task *DifyCreateTask) OnDifyRefreshStatusComplete(ctx context.Context, dif // 创建磁盘 for _, disk := range server.DisksInfo { volume := models.SVolume{} - volume.SvrId = disk.Id + volume.CmpId = disk.Id volume.LLMId = dify.Id volume.SizeMB = disk.SizeMb volume.Name = disk.Name diff --git a/pkg/llm/tasks/dify/dify_delete_task.go b/pkg/llm/tasks/dify/dify_delete_task.go index 4ecc8519e3..7428f5f95e 100644 --- a/pkg/llm/tasks/dify/dify_delete_task.go +++ b/pkg/llm/tasks/dify/dify_delete_task.go @@ -33,7 +33,7 @@ func (task *DifyDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, dify := obj.(*models.SDify) dify.SetStatus(ctx, task.UserCred, api.LLM_STATUS_DELETING, "start delete") - if len(dify.SvrId) == 0 { + if len(dify.CmpId) == 0 { task.OnDifyRefreshStatusComplete(ctx, dify, nil) return } diff --git a/pkg/llm/tasks/dify/dify_start_task.go b/pkg/llm/tasks/dify/dify_start_task.go index d00e497272..d633f910ce 100644 --- a/pkg/llm/tasks/dify/dify_start_task.go +++ b/pkg/llm/tasks/dify/dify_start_task.go @@ -45,7 +45,7 @@ func (t *DifyStartTask) requestStart(ctx context.Context, dify *models.SDify) { t.SetStage("OnStarted", nil) s := auth.GetSession(ctx, t.GetUserCred(), options.Options.Region) err := s.WithTaskCallback(t.GetId(), func() error { - _, err := compute.Servers.PerformAction(s, dify.SvrId, "start", nil) + _, err := compute.Servers.PerformAction(s, dify.CmpId, "start", nil) return err }) if err != nil { diff --git a/pkg/llm/tasks/dify/dify_stop_task.go b/pkg/llm/tasks/dify/dify_stop_task.go index 8751b065eb..ce4b58b6d6 100644 --- a/pkg/llm/tasks/dify/dify_stop_task.go +++ b/pkg/llm/tasks/dify/dify_stop_task.go @@ -57,7 +57,7 @@ func (task *DifyStopTask) OnInit(ctx context.Context, obj db.IStandaloneModel, b task.SetStage("OnStopComplete", nil) s := auth.GetSession(ctx, task.UserCred, "") s.WithTaskCallback(task.GetId(), func() error { - _, err = compute.Servers.PerformAction(s, dify.SvrId, "stop", nil) + _, err = compute.Servers.PerformAction(s, dify.CmpId, "stop", nil) return err }) if err != nil { diff --git a/pkg/llm/tasks/llm/llm_create_task.go b/pkg/llm/tasks/llm/llm_create_task.go index 9e7a2ecad1..8eaf8118be 100644 --- a/pkg/llm/tasks/llm/llm_create_task.go +++ b/pkg/llm/tasks/llm/llm_create_task.go @@ -56,10 +56,10 @@ func (task *LLMCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, } db.Update(llm, func() error { - llm.SvrId = serverId + llm.CmpId = serverId return nil }) - llm.SvrId = serverId + llm.CmpId = serverId return nil }) if err != nil { @@ -99,7 +99,7 @@ func (task *LLMCreateTask) OnLLMRefreshStatusComplete(ctx context.Context, llm * // 创建磁盘 for _, disk := range server.DisksInfo { volume := models.SVolume{} - volume.SvrId = disk.Id + volume.CmpId = disk.Id volume.LLMId = llm.Id volume.SizeMB = disk.SizeMb volume.Name = disk.Name diff --git a/pkg/llm/tasks/llm/llm_delete_task.go b/pkg/llm/tasks/llm/llm_delete_task.go index 3dab32d6a8..89c7c843cd 100644 --- a/pkg/llm/tasks/llm/llm_delete_task.go +++ b/pkg/llm/tasks/llm/llm_delete_task.go @@ -34,7 +34,7 @@ func (task *LLMDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, llm := obj.(*models.SLLM) llm.SetStatus(ctx, task.UserCred, api.LLM_STATUS_DELETING, "start delete") - if len(llm.SvrId) == 0 { + if len(llm.CmpId) == 0 { task.OnLLMRefreshStatusComplete(ctx, llm, nil) return } diff --git a/pkg/llm/tasks/llm/llm_start_task.go b/pkg/llm/tasks/llm/llm_start_task.go index 7c5818f8cf..a5c77d74ed 100644 --- a/pkg/llm/tasks/llm/llm_start_task.go +++ b/pkg/llm/tasks/llm/llm_start_task.go @@ -45,7 +45,7 @@ func (t *LLMStartTask) requestStart(ctx context.Context, llm *models.SLLM) { t.SetStage("OnStarted", nil) s := auth.GetSession(ctx, t.GetUserCred(), options.Options.Region) err := s.WithTaskCallback(t.GetId(), func() error { - _, err := compute.Servers.PerformAction(s, llm.SvrId, "start", nil) + _, err := compute.Servers.PerformAction(s, llm.CmpId, "start", nil) return err }) if err != nil { diff --git a/pkg/llm/tasks/llm/llm_stop_task.go b/pkg/llm/tasks/llm/llm_stop_task.go index a15f8cc4fe..5b147affe8 100644 --- a/pkg/llm/tasks/llm/llm_stop_task.go +++ b/pkg/llm/tasks/llm/llm_stop_task.go @@ -57,7 +57,7 @@ func (task *LLMStopTask) OnInit(ctx context.Context, obj db.IStandaloneModel, bo task.SetStage("OnStopComplete", nil) s := auth.GetSession(ctx, task.UserCred, "") err = s.WithTaskCallback(task.GetId(), func() error { - _, err = compute.Servers.PerformAction(s, llm.SvrId, "stop", nil) + _, err = compute.Servers.PerformAction(s, llm.CmpId, "stop", nil) return err }) if err != nil { diff --git a/pkg/llm/tasks/llm/llm_sync_status_task.go b/pkg/llm/tasks/llm/llm_sync_status_task.go index 45848b2ba6..3a2791e122 100644 --- a/pkg/llm/tasks/llm/llm_sync_status_task.go +++ b/pkg/llm/tasks/llm/llm_sync_status_task.go @@ -51,7 +51,7 @@ func (task *LLMSyncStatusTask) OnInit(ctx context.Context, obj db.IStandaloneMod task.setLLMStatus(ctx, llm, apis.LLM_STATUS_SYNCSTATUS, "LLMSyncStatusTask.OnInit") s := auth.GetSession(ctx, task.UserCred, "") - _, err := compute.Servers.PerformAction(s, llm.SvrId, "syncstatus", nil) + _, err := compute.Servers.PerformAction(s, llm.CmpId, "syncstatus", nil) if err != nil { task.taskFailed(ctx, llm, err.Error()) return @@ -93,7 +93,7 @@ func (task *LLMSyncStatusTask) OnInit(ctx context.Context, obj db.IStandaloneMod IsForce: true, TimeoutSecs: 10, } - _, err := compute.Servers.PerformAction(s, llm.SvrId, "stop", jsonutils.Marshal(params)) + _, err := compute.Servers.PerformAction(s, llm.CmpId, "stop", jsonutils.Marshal(params)) if err != nil { return nil, errors.Wrap(err, "ServerStop") } diff --git a/pkg/llm/tasks/volume/volume_delete_task.go b/pkg/llm/tasks/volume/volume_delete_task.go index d07ff5c601..d74e7db694 100644 --- a/pkg/llm/tasks/volume/volume_delete_task.go +++ b/pkg/llm/tasks/volume/volume_delete_task.go @@ -40,12 +40,12 @@ func (volumeDeleteTask *VolumeDeleteTask) taskComplete(ctx context.Context, volu func (volumeDeleteTask *VolumeDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { volume := obj.(*models.SVolume) - if len(volume.SvrId) == 0 { + if len(volume.CmpId) == 0 { volumeDeleteTask.taskComplete(ctx, volume) return } s := auth.GetSession(ctx, volumeDeleteTask.UserCred, "") - _, err := compute.Disks.Delete(s, volume.SvrId, nil) + _, err := compute.Disks.Delete(s, volume.CmpId, nil) if err != nil { if httputils.ErrorCode(err) == 404 { volumeDeleteTask.taskComplete(ctx, volume) @@ -55,7 +55,7 @@ func (volumeDeleteTask *VolumeDeleteTask) OnInit(ctx context.Context, obj db.ISt return } for i := 0; i < 60; i++ { - _, err := compute.Disks.GetById(s, volume.SvrId, jsonutils.Marshal(map[string]interface{}{ + _, err := compute.Disks.GetById(s, volume.CmpId, jsonutils.Marshal(map[string]interface{}{ "scope": "max", })) if err != nil { diff --git a/scripts/download_ollama_models.sh b/scripts/download_ollama_models.sh deleted file mode 100644 index 1ebc3cc457..0000000000 --- a/scripts/download_ollama_models.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/bin/bash - -# 脚本: Ollama 模型安全下载器 -# 描述: 此脚本根据输入的模型名称(例如 "qwen3:8b")安全地下载 Ollama 模型文件。 -# 它使用临时文件进行下载,并在成功后重命名,以防止文件损坏。 -# -# 用法: ./download_model.sh <模型名称:标签> -# 示例: ./download_model.sh qwen3:8b - -# --- 配置 --- - -# Ollama 注册表的基础 URL -LLM_OLLAMA_LIBRARY_BASE_URL="https://registry.ollama.ai/v2/library" -# 模型在主机上保存的基础路径 -LLM_OLLAMA_HOST_PATH="/opt/ollama-models" -# 主机上的清单目录 -LLM_OLLAMA_HOST_MANIFESTS_DIR="/manifests" -# 主机上的 blob 目录 -LLM_OLLAMA_HOST_BLOBS_DIR="/blobs" - -# --- 脚本 --- - -# 检查是否提供了模型名称作为参数 -if [ -z "$1" ]; then - echo "错误:未提供模型名称。" - echo "用法: $0 <模型名称:标签>" - exit 1 -fi - -# 从输入参数中解析模型名称和标签 -MODEL_FULL_NAME=$1 -MODEL_NAME=$(echo "$MODEL_FULL_NAME" | cut -d':' -f1) -MODEL_TAG=$(echo "$MODEL_FULL_NAME" | cut -d':' -f2) - -# 检查模型名称和标签是否已成功解析 -if [ "$MODEL_NAME" == "$MODEL_TAG" ] || [ -z "$MODEL_TAG" ]; then - echo "错误:模型名称格式无效。应为 '名称:标签' (例如 'qwen3:8b')。" - exit 1 -fi - -echo "开始安全下载模型: $MODEL_FULL_NAME" -echo "----------------------------------------" - -# --- 1. 下载清单文件 --- - -# 创建清单文件要保存的目录 (如果不存在) -MANIFEST_DIR="$LLM_OLLAMA_HOST_PATH$LLM_OLLAMA_HOST_MANIFESTS_DIR" -mkdir -p "$MANIFEST_DIR" - -# 构造清单文件的 URL、最终路径和临时路径 -MANIFEST_SUFFIX_URL="$MODEL_NAME/manifests/$MODEL_TAG" -MANIFEST_URL="$LLM_OLLAMA_LIBRARY_BASE_URL/$MANIFEST_SUFFIX_URL" -MANIFEST_FILE_PATH="$MANIFEST_DIR/$MODEL_NAME-$MODEL_TAG" -MANIFEST_FILE_PATH_TMP="${MANIFEST_FILE_PATH}.tmp" - -echo "步骤 1: 正在从 $MANIFEST_URL 下载清单..." - -# 检查最终文件是否已存在,如果存在则跳过 -if [ -f "$MANIFEST_FILE_PATH" ]; then - echo "清单文件已存在,跳过下载。" -else - # 使用 wget 下载到临时文件 - wget --quiet --show-progress -O "$MANIFEST_FILE_PATH_TMP" "$MANIFEST_URL" - # 检查 wget 的退出状态 - if [ $? -eq 0 ]; then - # 如果成功,重命名临时文件 - mv "$MANIFEST_FILE_PATH_TMP" "$MANIFEST_FILE_PATH" - echo "清单已成功下载到: $MANIFEST_FILE_PATH" - else - # 如果失败,打印错误并删除临时文件 - echo "错误:下载清单失败。请检查模型名称是否正确以及网络连接。" - rm -f "$MANIFEST_FILE_PATH_TMP" - exit 1 - fi -fi - -echo "----------------------------------------" - -# --- 2. 从清单中提取 Blob 的摘要 (digest) --- - -echo "步骤 2: 正在从清单文件中解析 blob 摘要..." -# 使用 grep 和 sed 通过正则表达式提取所有 blob 的 digest -BLOBS=$(grep -o '"digest":"sha256:[^"]*' "$MANIFEST_FILE_PATH" | sed 's/"digest":"//') - -if [ -z "$BLOBS" ]; then - echo "警告:在清单文件中未找到任何 blob 摘要。" - exit 0 -fi - -echo "已找到以下 blobs:" -echo "$BLOBS" -echo "----------------------------------------" - -# --- 3. 下载所有 Blob 文件 --- - -echo "步骤 3: 正在下载所有 blob 文件..." -# 创建 blob 文件要保存的目录 (如果不存在) -BLOBS_DIR="$LLM_OLLAMA_HOST_PATH$LLM_OLLAMA_HOST_BLOBS_DIR" -mkdir -p "$BLOBS_DIR" - -# 逐行遍历所有提取出的 blob 摘要 -for BLOB in $BLOBS; do - # 将 blob 摘要中的 "sha256:" 替换为 "sha256-" 以用作文件名 - BLOB_FILENAME=$(echo "$BLOB" | sed 's/sha256:/sha256-/') - BLOB_FILE_PATH="$BLOBS_DIR/$BLOB_FILENAME" - BLOB_FILE_PATH_TMP="${BLOB_FILE_PATH}.tmp" - - # 如果最终文件已经存在,则跳过下载 - if [ -f "$BLOB_FILE_PATH" ]; then - echo " 文件已存在, 跳过下载: $BLOB_FILENAME" - continue - fi - - # 构造 blob 的下载 URL - BLOB_URL="$LLM_OLLAMA_LIBRARY_BASE_URL/$MODEL_NAME/blobs/$BLOB" - - echo " 正在下载 $BLOB..." - # 使用 wget 下载 blob 到临时文件 - wget --quiet --show-progress -O "$BLOB_FILE_PATH_TMP" "$BLOB_URL" - - # 检查 wget 的退出状态 - if [ $? -eq 0 ]; then - # 如果成功,重命名临时文件 - mv "$BLOB_FILE_PATH_TMP" "$BLOB_FILE_PATH" - echo " 已成功保存到: $BLOB_FILE_PATH" - else - # 如果失败,打印错误并删除临时文件 - echo " 错误:下载 blob $BLOB 失败。已从 $BLOB_URL 尝试下载。" - rm -f "$BLOB_FILE_PATH_TMP" - # 如果希望在任何一个 blob 下载失败时立即中止整个脚本,请取消下一行的注释 - # exit 1 - fi -done - -echo "----------------------------------------" -echo "所有任务已完成。" -echo "模型 '$MODEL_FULL_NAME' 已成功下载到 '$LLM_OLLAMA_HOST_PATH'。" \ No newline at end of file