llm_container_driver.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package models
  2. import (
  3. "context"
  4. "fmt"
  5. "sync"
  6. commonapi "yunion.io/x/onecloud/pkg/apis"
  7. computeapi "yunion.io/x/onecloud/pkg/apis/compute"
  8. "yunion.io/x/onecloud/pkg/apis/llm"
  9. "yunion.io/x/onecloud/pkg/httperrors"
  10. "yunion.io/x/onecloud/pkg/mcclient"
  11. )
  12. type drivers struct {
  13. drivers *sync.Map
  14. }
  15. func newDrivers() *drivers {
  16. return &drivers{
  17. drivers: &sync.Map{},
  18. }
  19. }
  20. func (d *drivers) GetWithError(typ string) (interface{}, error) {
  21. drv, ok := d.drivers.Load(typ)
  22. if !ok {
  23. return drv, httperrors.NewNotFoundError("app container driver %s not found", typ)
  24. }
  25. return drv, nil
  26. }
  27. func (d *drivers) Get(typ string) interface{} {
  28. drv, err := d.GetWithError(typ)
  29. if err != nil {
  30. panic(err.Error())
  31. }
  32. return drv
  33. }
  34. func (d *drivers) Register(typ string, drv interface{}) {
  35. d.drivers.Store(typ, drv)
  36. }
  37. func registerDriver[K ~string, D any](drvs *drivers, typ K, drv D) {
  38. drvs.Register(string(typ), drv)
  39. }
  40. func getDriver[K ~string, D any](drvs *drivers, typ K) D {
  41. return drvs.Get(string(typ)).(D)
  42. }
  43. func getDriverWithError[K ~string, D any](drvs *drivers, typ K) (D, error) {
  44. drv, err := drvs.GetWithError(string(typ))
  45. if err != nil {
  46. return drv.(D), err
  47. }
  48. return drv.(D), nil
  49. }
  50. type ILLMContainerInstantModel interface {
  51. GetMountedModels(sku *SLLMSku) []string
  52. GetProbedInstantModelsExt(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, mdlIds ...string) (map[string]llm.LLMInternalInstantMdlInfo, error)
  53. DetectModelPaths(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, pkgInfo llm.LLMInternalInstantMdlInfo) ([]string, error)
  54. GetImageInternalPathMounts(sApp *SInstantModel) map[string]string
  55. GetSaveDirectories(sApp *SInstantModel) (string, []string, error)
  56. ValidateMounts(mounts []string, mdlName string, mdlTag string) ([]string, error)
  57. CheckDuplicateMounts(errStr string, dupIndex int) string
  58. GetInstantModelIdByPostOverlay(postOverlay *commonapi.ContainerVolumeMountDiskPostOverlay, mdlNameToId map[string]string) string
  59. GetDirPostOverlay(dir llm.LLMMountDirInfo) *commonapi.ContainerVolumeMountDiskPostOverlay
  60. PreInstallModel(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, instMdl *SLLMInstantModel) error
  61. InstallModel(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, dirs []string, mdlIds []string) error
  62. UninstallModel(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, instMdl *SLLMInstantModel) error
  63. DownloadModel(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, tmpDir string, modelName string, modelTag string) (string, []string, error)
  64. }
  65. type ILLMContainerDriver interface {
  66. GetType() llm.LLMContainerType
  67. // GetContainerSpecs returns one or more container specs. If nil or empty, caller falls back to GetContainerSpec for a single container.
  68. GetContainerSpecs(ctx context.Context, llm *SLLM, image *SLLMImage, sku *SLLMSku, props []string, devices []computeapi.SIsolatedDevice, diskId string) []*computeapi.PodContainerCreateInput
  69. GetPrimaryContainer(ctx context.Context, llm *SLLM, containers []*computeapi.PodContainerDesc) (*computeapi.PodContainerDesc, error)
  70. // StartLLM is called after the pod is running. For drivers that need to start the model process inside the container (e.g. vLLM), it runs the start command via exec and waits for health; on failure returns an error. For drivers that need no extra step (e.g. Ollama), it returns nil.
  71. StartLLM(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM) error
  72. // GetSpec returns the type-specific spec from the SKU (e.g. *LLMSpecOllama, *LLMSpecDify). Returns nil if not applicable or missing.
  73. GetSpec(sku *SLLMSku) interface{}
  74. // GetEffectiveSpec returns the merged type-specific spec for container build: llm.LLMSpec and sku.LLMSpec merged with llm priority; each driver implements its own merge. Returns same type as GetSpec.
  75. GetEffectiveSpec(llm *SLLM, sku *SLLMSku) interface{}
  76. // GetPrimaryImageId returns the primary image id for this SKU type (e.g. LLMImageId for ollama/vllm, DifyApiImageId for dify).
  77. GetPrimaryImageId(sku *SLLMSku) string
  78. // ValidateLLMSkuCreateData validates create input and returns the LLMSpec to store. Called by SKU manager after base validation.
  79. ValidateLLMSkuCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *llm.LLMSkuCreateInput) (*llm.LLMSkuCreateInput, error)
  80. // ValidateLLMSkuUpdateData validates update input, merges with current spec, and returns the LLMSpec to store. Called by SKU when LLMSpec is not nil.
  81. ValidateLLMSkuUpdateData(ctx context.Context, userCred mcclient.TokenCredential, sku *SLLMSku, input *llm.LLMSkuUpdateInput) (*llm.LLMSkuUpdateInput, error)
  82. MatchContainerToUpdate(ctr *computeapi.SContainer, podCtrs []*computeapi.PodContainerCreateInput) (*computeapi.PodContainerCreateInput, error)
  83. ValidateLLMCreateSpec(ctx context.Context, userCred mcclient.TokenCredential, sku *SLLMSku, input *llm.LLMSpec) (*llm.LLMSpec, error)
  84. ValidateLLMUpdateSpec(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, input *llm.LLMSpec) (*llm.LLMSpec, error)
  85. ILLMContainerMCPAgent
  86. }
  87. // ILLMContainerInstantModelDriver is for drivers that support instant models (e.g. Ollama, vLLM). GetMountedModels is only required here so that drivers without models (e.g. Dify) need not implement it.
  88. type ILLMContainerInstantModelDriver interface {
  89. ILLMContainerDriver
  90. ILLMContainerInstantModel
  91. }
  92. type ILLMContainerMCPAgent interface {
  93. GetLLMAccessUrlInfo(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, input *LLMAccessInfoInput) (*llm.LLMAccessUrlInfo, error)
  94. }
  95. // ILLMContainerLoginInfo is an optional interface for drivers that provide web login credentials (e.g. Dify, OpenClaw). If not implemented, GetDetailsLoginInfo returns only login_url.
  96. type ILLMContainerLoginInfo interface {
  97. GetLoginInfo(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM) (*llm.LLMAccessInfo, error)
  98. }
  99. var (
  100. llmContainerDrivers = newDrivers()
  101. )
  102. func RegisterLLMContainerDriver(drv ILLMContainerDriver) {
  103. registerDriver(llmContainerDrivers, drv.GetType(), drv)
  104. }
  105. func GetLLMContainerDriver(typ llm.LLMContainerType) ILLMContainerDriver {
  106. return getDriver[llm.LLMContainerType, ILLMContainerDriver](llmContainerDrivers, typ)
  107. }
  108. func GetLLMContainerDriverWithError(typ llm.LLMContainerType) (ILLMContainerDriver, error) {
  109. return getDriverWithError[llm.LLMContainerType, ILLMContainerDriver](llmContainerDrivers, typ)
  110. }
  111. func GetLLMContainerInstantModelDriver(typ llm.LLMContainerType) (ILLMContainerInstantModelDriver, error) {
  112. drv, err := GetLLMContainerDriverWithError(typ)
  113. if err != nil {
  114. return nil, err
  115. }
  116. if instantDrv, ok := drv.(ILLMContainerInstantModelDriver); ok {
  117. return instantDrv, nil
  118. }
  119. return nil, fmt.Errorf("driver %s does not support instant model operations", typ)
  120. }
  121. // GetDriverPodContainers returns the container(s) for the given driver. If the driver implements ILLMContainerDriverMultiContainer, GetContainerSpecs is used; otherwise a single-element slice from GetContainerSpec is returned.
  122. func GetDriverPodContainers(ctx context.Context, drv ILLMContainerDriver, llm *SLLM, image *SLLMImage, sku *SLLMSku, props []string, devices []computeapi.SIsolatedDevice, diskId string) []*computeapi.PodContainerCreateInput {
  123. return drv.GetContainerSpecs(ctx, llm, image, sku, props, devices, diskId)
  124. }