Files
cloudpods/pkg/llm/models/image.go
T
cwz_eikoh 8c20fb7e3d Automated cherry pick of #23906: feature(llm): adapt frontend (#24095)
* feature(llm): add llm-list details, add autostart for llm-save-instant-model

* fix(llm): adjust some interfaces

* fix: name-dup problem when create llm

* fix: install instant-model by id rather than modelID

* fix(llm): add llm_id for mcp-agent

* feature(llm): move network from sku to instance

* feature(llm): add LLMType for llm-image

* feature(llm): add gpuMemoryRequired & ollama-registry yaml

* feature(llm): add url-get interface

* feature(llm): support mcp in mcp-agent-chat

* fix(llm): abstract ollama registry
2026-01-19 18:06:18 +08:00

173 lines
5.6 KiB
Go

package models
import (
"context"
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/sqlchemy"
identityapi "yunion.io/x/onecloud/pkg/apis/identity"
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/llm/options"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules/identity"
)
func init() {
GetLLMImageManager()
}
var llmImageManager *SLLMImageManager
func GetLLMImageManager() *SLLMImageManager {
if llmImageManager != nil {
return llmImageManager
}
llmImageManager = &SLLMImageManager{
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
SLLMImage{},
"llm_images_tbl",
"llm_image",
"llm_images",
),
}
llmImageManager.SetVirtualObject(llmImageManager)
return llmImageManager
}
type SLLMImageManager struct {
db.SSharableVirtualResourceBaseManager
}
type SLLMImage struct {
db.SSharableVirtualResourceBase
ImageName string `width:"128" charset:"utf8" nullable:"false" list:"user" create:"admin_optional" update:"user"`
ImageLabel string `width:"64" charset:"utf8" nullable:"false" list:"user" create:"admin_optional" update:"user"`
CredentialId string `width:"128" charset:"utf8" nullable:"true" list:"user" create:"admin_optional" update:"user"`
LLMType string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"admin_optional" update:"user"`
}
func fetchImageCredential(ctx context.Context, userCred mcclient.TokenCredential, cid string) (*identityapi.CredentialDetails, error) {
s := auth.GetSession(ctx, userCred, options.Options.Region)
credJson, err := identity.Credentials.Get(s, cid, nil)
if err != nil {
return nil, errors.Wrap(err, "Credentials.Get")
}
details := identityapi.CredentialDetails{}
err = credJson.Unmarshal(&details)
if err != nil {
return nil, errors.Wrap(err, "Unmarshal")
}
return &details, nil
}
func (man *SLLMImageManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.LLMImageCreateInput) (*api.LLMImageCreateInput, error) {
var err error
input.SharableVirtualResourceCreateInput, err = man.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
if nil != err {
return input, errors.Wrap(err, "validate SharableVirtualResourceCreateInput")
}
if len(input.CredentialId) > 0 {
cred, err := fetchImageCredential(ctx, userCred, input.CredentialId)
if err != nil {
return input, errors.Wrap(err, "fetchImageCredential")
}
input.CredentialId = cred.Id
}
if len(input.LLMType) > 0 {
if !api.IsLLMImageType(input.LLMType) {
return input, errors.Wrap(httperrors.ErrInputParameter, "llm_type must be one of "+strings.Join(api.LLM_IMAGE_TYPES.List(), ","))
}
}
input.Status = api.STATUS_READY
return input, nil
}
func (man *SLLMImageManager) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.LLMImageUpdateInput) (*api.LLMImageUpdateInput, error) {
var err error
input.SharableVirtualResourceCreateInput, err = man.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
if nil != err {
return input, errors.Wrap(err, "validate SharableVirtualResourceCreateInput")
}
if nil != input.CredentialId && len(*input.CredentialId) > 0 {
cred, err := fetchImageCredential(ctx, userCred, *input.CredentialId)
if err != nil {
return input, errors.Wrap(err, "fetchImageCredential")
}
input.CredentialId = &cred.Id
}
if nil != input.LLMType && len(*input.LLMType) > 0 {
if !api.IsLLMImageType(*input.LLMType) {
return input, errors.Wrap(httperrors.ErrInputParameter, "llm_type must be one of "+strings.Join(api.LLM_IMAGE_TYPES.List(), ","))
}
}
return input, nil
}
func (man *SLLMImageManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
input api.LLMImageListInput,
) (*sqlchemy.SQuery, error) {
q, err := man.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.SharableVirtualResourceListInput)
if err != nil {
return nil, errors.Wrapf(err, "SSharableBaseResourceManager.ListItemFilter")
}
if input.IsPublic != nil {
if *input.IsPublic {
q = q.IsTrue("is_public")
} else {
q = q.IsFalse("is_public")
}
}
if len(input.ImageLabel) > 0 {
q = q.Equals("image_label", input.ImageLabel)
}
if len(input.ImageName) > 0 {
q = q.Equals("image_name", input.ImageName)
}
if len(input.LLMType) > 0 {
q = q.Equals("llm_type", input.LLMType)
}
return q, nil
}
func (image *SLLMImage) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
for _, field := range []string{"llm_image_id"} {
count, err := GetLLMManager().Query().Equals(field, image.Id).CountWithError()
if err != nil {
return errors.Wrap(err, "fetch llms")
}
if count > 0 {
return errors.Wrapf(errors.ErrNotSupported, "This image is currently in use by %s in llms", field)
}
count, err = GetLLMSkuManager().Query().Equals("llm_image_id", image.Id).CountWithError()
if err != nil {
return errors.Wrap(err, "fetch llm models")
}
if count > 0 {
return errors.Wrapf(errors.ErrNotSupported, "This image is currently in use by %s in llm models", field)
}
}
return nil
}
func (image *SLLMImage) ToContainerImage() string {
return fmt.Sprintf("%s:%s", image.ImageName, image.ImageLabel)
}