mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-31 01:35:56 +08:00
feat(llm): create llm_sku from llm_model_set (#24865)
Co-authored-by: cwz <cwz_eikoh@163.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/util/printutils"
|
||||
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/llm"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/llm"
|
||||
@@ -11,7 +14,7 @@ func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.LLMModelSets)
|
||||
cmd.List(new(options.LLMModelSetListOptions))
|
||||
cmd.Show(new(options.LLMModelSetShowOptions))
|
||||
cmd.Get("specs", new(options.LLMModelSetSpecsOptions))
|
||||
cmd.GetWithCustomShow("specs", printModelSetSpecs, new(options.LLMModelSetSpecsOptions))
|
||||
cmd.PerformClass("refresh", new(options.LLMModelSetRefreshOptions))
|
||||
}
|
||||
{
|
||||
@@ -19,3 +22,15 @@ func init() {
|
||||
cmd.Show(new(options.LLMModelSpecShowOptions))
|
||||
}
|
||||
}
|
||||
|
||||
func printModelSetSpecs(data jsonutils.JSONObject) {
|
||||
specs, _ := data.GetArray("llm_model_specs")
|
||||
total, _ := data.Int("total")
|
||||
if total == 0 {
|
||||
total = int64(len(specs))
|
||||
}
|
||||
shell.PrintList(&printutils.ListResult{
|
||||
Data: specs,
|
||||
Total: int(total),
|
||||
}, []string{"id", "label", "quantization", "mode", "backend", "source", "huggingface_repo_id"})
|
||||
}
|
||||
|
||||
@@ -11,11 +11,15 @@ type LLMCatalogDoc struct {
|
||||
// (e.g. "Qwen3-8B") that may be deployed via one or more specs.
|
||||
// Schema mirrors GPUStack `ModelSet` / `ModelSetBase`.
|
||||
type LLMModelSet struct {
|
||||
// Resource id exposed to mcclient / climc. The upstream catalog's logical
|
||||
// key is name, so we mirror it as id for standard resource commands.
|
||||
Id string `json:"id,omitempty" yaml:"-"`
|
||||
|
||||
// Required: globally-unique identifier from YAML. Used as the resource id.
|
||||
Name string `json:"name" yaml:"name"`
|
||||
|
||||
// Optional numeric id assigned by the upstream catalog author.
|
||||
Id int `json:"id,omitempty" yaml:"id,omitempty"`
|
||||
CatalogId int `json:"catalog_id,omitempty" yaml:"id,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Order int `json:"order,omitempty" yaml:"order,omitempty"`
|
||||
Home string `json:"home,omitempty" yaml:"home,omitempty"`
|
||||
@@ -86,6 +90,10 @@ type LLMModelSpec struct {
|
||||
|
||||
// --- Server-assigned, NOT in upstream YAML ---
|
||||
|
||||
// Id mirrors SpecId for standard resource-list columns.
|
||||
Id string `json:"id,omitempty" yaml:"-"`
|
||||
// Label is a concise display label for climc/UI tables.
|
||||
Label string `json:"label,omitempty" yaml:"-"`
|
||||
// SpecId is a synthetic stable id generated at load time so the frontend
|
||||
// can address a single spec via /llm_model_specs/<id>. Slugified from
|
||||
// (set name, mode, quantization, backend). Not present in source YAML.
|
||||
|
||||
@@ -126,6 +126,7 @@ type LLMSkuDetails struct {
|
||||
LLMSpec *LLMSpec `json:"llm_spec,omitempty"`
|
||||
|
||||
// Model source
|
||||
LLMModelSpecId string `json:"llm_model_spec_id"`
|
||||
Source string `json:"source"`
|
||||
HuggingfaceRepoId string `json:"huggingface_repo_id"`
|
||||
HuggingfaceFilename string `json:"huggingface_filename"`
|
||||
@@ -196,6 +197,8 @@ type LLMSkuCreateInput struct {
|
||||
|
||||
LLMImageId string `json:"llm_image_id"`
|
||||
LLMType string `json:"llm_type"`
|
||||
// LLMModelSpecId references one deployable spec from the in-memory model catalog.
|
||||
LLMModelSpecId string `json:"llm_model_spec_id"`
|
||||
|
||||
// LLMSpec:
|
||||
// - ollama/vllm: backend builds llm_spec from llm_image_id + mounted_models; for vllm preferred model should be set in llm_spec.vllm.preferred_model.
|
||||
@@ -228,6 +231,7 @@ type LLMSkuUpdateInput struct {
|
||||
LLMSpec *LLMSpec `json:"llm_spec,omitempty"`
|
||||
|
||||
// Model source
|
||||
LLMModelSpecId *string `json:"llm_model_spec_id,omitempty"`
|
||||
Source *string `json:"source,omitempty"`
|
||||
HuggingfaceRepoId *string `json:"huggingface_repo_id,omitempty"`
|
||||
HuggingfaceFilename *string `json:"huggingface_filename,omitempty"`
|
||||
|
||||
@@ -145,6 +145,7 @@ func (m *SLLMModelSetManager) Refresh(ctx context.Context) error {
|
||||
if s.Name == "" {
|
||||
continue
|
||||
}
|
||||
s.Id = s.Name
|
||||
sets = append(sets, s)
|
||||
setRef := &sets[len(sets)-1]
|
||||
setsByName[s.Name] = setRef
|
||||
@@ -161,6 +162,8 @@ func (m *SLLMModelSetManager) Refresh(ctx context.Context) error {
|
||||
id = fmt.Sprintf("%s-%d", base, seen+1)
|
||||
}
|
||||
usedIds[base]++
|
||||
sp.Id = id
|
||||
sp.Label = modelSpecLabel(sp, id)
|
||||
sp.SpecId = id
|
||||
specsById[id] = &specRef{SetName: setRef.Name, Spec: sp}
|
||||
totalSpecs++
|
||||
@@ -179,6 +182,15 @@ func (m *SLLMModelSetManager) Refresh(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func modelSpecLabel(sp *api.LLMModelSpec, id string) string {
|
||||
for _, s := range []string{sp.Name, sp.Quantization, sp.Mode, sp.Backend, id} {
|
||||
if strings.TrimSpace(s) != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// specBaseId composes a slug from the spec's identifying fields. Stable across
|
||||
// loads as long as the upstream YAML keeps the same (set.name, mode,
|
||||
// quantization, backend) tuple per spec.
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
imageapi "yunion.io/x/onecloud/pkg/apis/image"
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
@@ -57,6 +58,7 @@ type SLLMSku struct {
|
||||
LLMSpec *api.LLMSpec `json:"llm_spec" length:"long" list:"user" create:"optional" update:"user"`
|
||||
|
||||
// Model source
|
||||
LLMModelSpecId string `width:"256" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"`
|
||||
Source string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
|
||||
HuggingfaceRepoId string `width:"256" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"`
|
||||
HuggingfaceFilename string `width:"256" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"`
|
||||
@@ -140,6 +142,16 @@ func (manager *SLLMSkuManager) FetchCustomizeColumns(
|
||||
res[i].SharableVirtualResourceDetails = virows[i]
|
||||
res[i].LLMType = sku.LLMType
|
||||
res[i].LLMSpec = sku.LLMSpec
|
||||
res[i].LLMModelSpecId = sku.LLMModelSpecId
|
||||
res[i].Source = sku.Source
|
||||
res[i].HuggingfaceRepoId = sku.HuggingfaceRepoId
|
||||
res[i].HuggingfaceFilename = sku.HuggingfaceFilename
|
||||
res[i].ModelScopeModelId = sku.ModelScopeModelId
|
||||
res[i].ModelScopeFilePath = sku.ModelScopeFilePath
|
||||
res[i].LocalPath = sku.LocalPath
|
||||
res[i].Categories = sku.Categories
|
||||
res[i].BackendVersion = sku.BackendVersion
|
||||
res[i].BackendParameters = sku.BackendParameters
|
||||
for _, v := range details {
|
||||
if v.LLMSkuId == sku.Id {
|
||||
res[i].LLMCapacity = v.LLMCapacity
|
||||
@@ -228,10 +240,53 @@ func (man *SLLMSkuManager) ValidateCreateData(ctx context.Context, userCred mccl
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validate create input")
|
||||
}
|
||||
input.Status = api.STATUS_READY
|
||||
if _, err := resolveLLMSkuCatalogImport(input); err != nil {
|
||||
return input, err
|
||||
}
|
||||
if input.LLMModelSpecId != "" {
|
||||
input.Status = api.LLM_DEPLOYMENT_STATUS_IMPORTING_MODEL
|
||||
} else {
|
||||
input.Status = api.STATUS_READY
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (sku *SLLMSku) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
sku.SSharableVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
input := api.LLMSkuCreateInput{}
|
||||
if data == nil || data.Unmarshal(&input) != nil || input.LLMModelSpecId == "" {
|
||||
return
|
||||
}
|
||||
if err := sku.StartCreateTask(ctx, userCred, data); err != nil {
|
||||
log.Errorf("SLLMSku.PostCreate start task failed: %s", err)
|
||||
sku.SetStatus(ctx, userCred, api.LLM_DEPLOYMENT_STATUS_IMPORT_MODEL_FAILED, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (sku *SLLMSku) StartCreateTask(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error {
|
||||
params, _ := data.(*jsonutils.JSONDict)
|
||||
if params == nil {
|
||||
params = jsonutils.NewDict()
|
||||
}
|
||||
importInput := api.InstantModelImportInput{}
|
||||
if err := params.Unmarshal(&importInput, "import_input"); err != nil {
|
||||
createdInput := api.LLMSkuCreateInput{}
|
||||
if err := params.Unmarshal(&createdInput); err != nil {
|
||||
return errors.Wrap(err, "unmarshal LLMSkuCreateInput")
|
||||
}
|
||||
derived, err := resolveLLMSkuCatalogImport(&createdInput)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "resolve catalog import")
|
||||
}
|
||||
params.Set("import_input", jsonutils.Marshal(derived))
|
||||
}
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "LLMSkuCreateTask", sku, userCred, params, "", "", nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewTask LLMSkuCreateTask")
|
||||
}
|
||||
return task.ScheduleRun(nil)
|
||||
}
|
||||
|
||||
// GetLLMImageId returns the primary image id for this SKU. Delegates to driver.
|
||||
func (sku *SLLMSku) GetLLMImageId() string {
|
||||
return sku.GetLLMContainerDriver().GetPrimaryImageId(sku)
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
func resolveLLMSkuCatalogImport(input *api.LLMSkuCreateInput) (*api.InstantModelImportInput, error) {
|
||||
if input == nil || strings.TrimSpace(input.LLMModelSpecId) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
spec, setName, ok := GetLLMModelSetManager().GetSpec(input.LLMModelSpecId)
|
||||
if !ok {
|
||||
return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "llm_model_spec %s not found", input.LLMModelSpecId)
|
||||
}
|
||||
set, ok := GetLLMModelSetManager().GetSet(setName)
|
||||
if !ok {
|
||||
return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "llm_model_set %s not found", setName)
|
||||
}
|
||||
return buildLLMSkuCatalogImport(input, set, spec)
|
||||
}
|
||||
|
||||
func catalogBackendToLLMType(backend string) (string, bool) {
|
||||
switch strings.ToLower(strings.TrimSpace(backend)) {
|
||||
case "":
|
||||
return "", true
|
||||
case "vllm":
|
||||
return string(api.LLM_CONTAINER_VLLM), true
|
||||
case "sglang":
|
||||
return string(api.LLM_CONTAINER_SGLANG), true
|
||||
case "ollama":
|
||||
return string(api.LLM_CONTAINER_OLLAMA), true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
func buildLLMSkuCatalogImport(input *api.LLMSkuCreateInput, set *api.LLMModelSet, spec *api.LLMModelSpec) (*api.InstantModelImportInput, error) {
|
||||
if input == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInputParameter, "empty sku input")
|
||||
}
|
||||
if spec == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInputParameter, "empty catalog spec")
|
||||
}
|
||||
if !strings.EqualFold(spec.Source, api.LLM_MODEL_SOURCE_HUGGINGFACE) {
|
||||
return nil, errors.Wrapf(httperrors.ErrInputParameter, "unsupported catalog source %q", spec.Source)
|
||||
}
|
||||
if strings.TrimSpace(spec.HuggingfaceRepoId) == "" {
|
||||
return nil, errors.Wrap(httperrors.ErrMissingParameter, "huggingface_repo_id is required")
|
||||
}
|
||||
if expectedType, ok := catalogBackendToLLMType(spec.Backend); !ok {
|
||||
return nil, errors.Wrapf(httperrors.ErrInputParameter, "unsupported catalog backend %q", spec.Backend)
|
||||
} else if expectedType != "" && input.LLMType != "" && expectedType != input.LLMType {
|
||||
return nil, errors.Wrapf(httperrors.ErrInputParameter, "catalog backend %q requires llm_type %q", spec.Backend, expectedType)
|
||||
}
|
||||
|
||||
input.LLMModelSpecId = spec.SpecId
|
||||
input.Source = spec.Source
|
||||
input.HuggingfaceRepoId = spec.HuggingfaceRepoId
|
||||
input.HuggingfaceFilename = spec.HuggingfaceFilename
|
||||
input.ModelScopeModelId = spec.ModelScopeModelId
|
||||
input.ModelScopeFilePath = spec.ModelScopeFilePath
|
||||
input.LocalPath = spec.LocalPath
|
||||
input.BackendVersion = spec.BackendVersion
|
||||
input.BackendParameters = append([]string{}, spec.BackendParameters...)
|
||||
if set != nil {
|
||||
input.Categories = append([]string{}, set.Categories...)
|
||||
}
|
||||
|
||||
revision := defaultHuggingFaceRevision
|
||||
return &api.InstantModelImportInput{
|
||||
ModelName: spec.HuggingfaceRepoId,
|
||||
ModelTag: revision,
|
||||
LlmType: api.LLMContainerType(input.LLMType),
|
||||
Source: api.InstantModelSourceHuggingFace,
|
||||
RepoId: spec.HuggingfaceRepoId,
|
||||
Revision: revision,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func appendMountedModelIds(existing []string, ids ...string) []string {
|
||||
out := append([]string{}, existing...)
|
||||
seen := make(map[string]struct{}, len(out)+len(ids))
|
||||
for _, id := range out {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
for _, id := range ids {
|
||||
id = strings.TrimSpace(id)
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
out = append(out, id)
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func EnableInstantModelForUse(ctx context.Context, userCred mcclient.TokenCredential, id string) error {
|
||||
obj, err := GetInstantModelManager().FetchById(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "fetch InstantModel %s", id)
|
||||
}
|
||||
im := obj.(*SInstantModel)
|
||||
if im.Enabled.IsTrue() {
|
||||
return nil
|
||||
}
|
||||
_, err = db.Update(im, func() error {
|
||||
im.SetEnabled(true)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "db.Update")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sku *SLLMSku) AttachMountedModel(ctx context.Context, userCred mcclient.TokenCredential, instantModelId string) error {
|
||||
_, err := db.Update(sku, func() error {
|
||||
sku.MountedModels = appendMountedModelIds(sku.MountedModels, instantModelId)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "db.Update")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -177,7 +177,7 @@ func (task *LLMDeploymentCreateTask) OnInstantModelReady(ctx context.Context, mo
|
||||
// Auto-enable the freshly-imported InstantModel — without this it stays
|
||||
// disabled and FindReadyInstantModel can't dedup it on the next catalog
|
||||
// deploy, plus the SKU/LLM mount validators depend on enabled rows.
|
||||
if err := enableInstantModel(ctx, task.UserCred, instantId); err != nil {
|
||||
if err := models.EnableInstantModelForUse(ctx, task.UserCred, instantId); err != nil {
|
||||
log.Warningf("LLMDeploymentCreateTask: enable InstantModel %s: %s", instantId, err)
|
||||
}
|
||||
|
||||
@@ -187,28 +187,6 @@ func (task *LLMDeploymentCreateTask) OnInstantModelReady(ctx context.Context, mo
|
||||
task.createSkuAndReconcile(ctx, model, &skuSpec, task.GetParams())
|
||||
}
|
||||
|
||||
// enableInstantModel flips the InstantModel's enabled flag to true after a
|
||||
// successful catalog-driven import. Idempotent: skips the update if already
|
||||
// enabled.
|
||||
func enableInstantModel(ctx context.Context, userCred mcclient.TokenCredential, id string) error {
|
||||
obj, err := models.GetInstantModelManager().FetchById(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "fetch InstantModel %s", id)
|
||||
}
|
||||
im := obj.(*models.SInstantModel)
|
||||
if im.Enabled.IsTrue() {
|
||||
return nil
|
||||
}
|
||||
_, err = db.Update(im, func() error {
|
||||
im.SetEnabled(true)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "db.Update")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnInstantModelReadyFailed is called if the child InstantModel import task fails.
|
||||
func (task *LLMDeploymentCreateTask) OnInstantModelReadyFailed(ctx context.Context, model *models.SLLMDeployment, body jsonutils.JSONObject) {
|
||||
task.taskFailedImporting(ctx, model, fmt.Errorf("InstantModel import failed: %s", body))
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/llm/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type LLMSkuCreateTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(LLMSkuCreateTask{})
|
||||
}
|
||||
|
||||
func (task *LLMSkuCreateTask) taskFailed(ctx context.Context, sku *models.SLLMSku, err error) {
|
||||
sku.SetStatus(ctx, task.UserCred, api.LLM_DEPLOYMENT_STATUS_IMPORT_MODEL_FAILED, err.Error())
|
||||
db.OpsLog.LogEvent(sku, db.ACT_CREATE, err, task.UserCred)
|
||||
logclient.AddActionLogWithStartable(task, sku, logclient.ACT_CREATE, err, task.UserCred, false)
|
||||
task.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
func (task *LLMSkuCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
sku := obj.(*models.SLLMSku)
|
||||
importInput := api.InstantModelImportInput{}
|
||||
if err := task.GetParams().Unmarshal(&importInput, "import_input"); err != nil {
|
||||
task.taskFailed(ctx, sku, errors.Wrap(err, "unmarshal import_input"))
|
||||
return
|
||||
}
|
||||
if importInput.LlmType == "" {
|
||||
importInput.LlmType = api.LLMContainerType(sku.LLMType)
|
||||
}
|
||||
|
||||
if existing, err := models.GetInstantModelManager().FindReadyInstantModel(
|
||||
string(importInput.LlmType),
|
||||
importInput.ModelName,
|
||||
importInput.ModelTag,
|
||||
); err != nil {
|
||||
log.Warningf("LLMSkuCreateTask FindReadyInstantModel: %s; importing a fresh InstantModel", err)
|
||||
} else if existing != nil {
|
||||
task.completeWithInstantModel(ctx, sku, existing.GetId())
|
||||
return
|
||||
}
|
||||
|
||||
task.SetStage("OnInstantModelReady", nil)
|
||||
instantModel, err := models.GetInstantModelManager().DoImportWithParent(ctx, task.UserCred, importInput, task.GetTaskId())
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, sku, errors.Wrap(err, "DoImportWithParent"))
|
||||
return
|
||||
}
|
||||
extra := jsonutils.NewDict()
|
||||
extra.Set("imported_instant_model_id", jsonutils.NewString(instantModel.GetId()))
|
||||
if err := task.SaveParams(extra); err != nil {
|
||||
log.Warningf("LLMSkuCreateTask persist imported instant model id: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (task *LLMSkuCreateTask) OnInstantModelReady(ctx context.Context, sku *models.SLLMSku, body jsonutils.JSONObject) {
|
||||
instantId, _ := task.GetParams().GetString("imported_instant_model_id")
|
||||
if instantId == "" {
|
||||
task.taskFailed(ctx, sku, errors.Error("missing imported_instant_model_id in task params"))
|
||||
return
|
||||
}
|
||||
task.completeWithInstantModel(ctx, sku, instantId)
|
||||
}
|
||||
|
||||
func (task *LLMSkuCreateTask) OnInstantModelReadyFailed(ctx context.Context, sku *models.SLLMSku, body jsonutils.JSONObject) {
|
||||
task.taskFailed(ctx, sku, fmt.Errorf("InstantModel import failed: %s", body))
|
||||
}
|
||||
|
||||
func (task *LLMSkuCreateTask) completeWithInstantModel(ctx context.Context, sku *models.SLLMSku, instantId string) {
|
||||
if err := models.EnableInstantModelForUse(ctx, task.UserCred, instantId); err != nil {
|
||||
task.taskFailed(ctx, sku, errors.Wrapf(err, "enable InstantModel %s", instantId))
|
||||
return
|
||||
}
|
||||
if err := sku.AttachMountedModel(ctx, task.UserCred, instantId); err != nil {
|
||||
task.taskFailed(ctx, sku, errors.Wrapf(err, "attach InstantModel %s to SKU", instantId))
|
||||
return
|
||||
}
|
||||
if err := sku.SetStatus(ctx, task.UserCred, api.STATUS_READY, "instant model imported"); err != nil {
|
||||
task.taskFailed(ctx, sku, errors.Wrap(err, "set sku ready"))
|
||||
return
|
||||
}
|
||||
db.OpsLog.LogEvent(sku, db.ACT_CREATE, sku.GetShortDesc(ctx), task.UserCred)
|
||||
logclient.AddActionLogWithStartable(task, sku, logclient.ACT_CREATE, sku.GetShortDesc(ctx), task.UserCred, true)
|
||||
task.SetStageComplete(ctx, nil)
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
@@ -30,6 +35,19 @@ type LLMModelSetsManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
func (m *LLMModelSetsManager) GetSpecific(session *mcclient.ClientSession, id string, spec string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
if spec != "specs" {
|
||||
return m.ResourceManager.GetSpecific(session, id, spec, params)
|
||||
}
|
||||
path := fmt.Sprintf("/%s/%s/%s", m.ContextPath(nil), url.PathEscape(id), url.PathEscape(spec))
|
||||
if params != nil {
|
||||
if qs := params.QueryString(); qs != "" {
|
||||
path = fmt.Sprintf("%s?%s", path, qs)
|
||||
}
|
||||
}
|
||||
return modulebase.Get(m.ResourceManager, session, path, "")
|
||||
}
|
||||
|
||||
type LLMModelSpecsManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ type LLMSkuCreateOptions struct {
|
||||
LLM_TYPE string `json:"llm_type" choices:"ollama|vllm|comfyui|sglang"`
|
||||
|
||||
// Model source
|
||||
LLMModelSpecId string `help:"LLM catalog model spec id; starts InstantModel import and mounts it into this SKU" json:"llm_model_spec_id"`
|
||||
Source string `help:"model source: huggingface, model_scope, local_path" json:"source"`
|
||||
HuggingfaceRepoId string `help:"HuggingFace repo ID" json:"huggingface_repo_id"`
|
||||
HuggingfaceFilename string `help:"HuggingFace filename" json:"huggingface_filename"`
|
||||
|
||||
Reference in New Issue
Block a user