mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-31 01:35:56 +08:00
* feat(llm): polymetric dify with llm * fix(llm): drop LLMSpecHolder & fix some bugs --------- Co-authored-by: cwz <cwz_eikoh@163.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.Difies)
|
||||
cmd := shell.NewResourceCmd(&modules.LLMs).WithKeyword("dify")
|
||||
cmd.BatchCreate(new(options.DifyCreateOptions))
|
||||
cmd.List(new(options.DifyListOptions))
|
||||
cmd.Show(new(options.DifyShowOptions))
|
||||
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/llm"
|
||||
)
|
||||
|
||||
// dify-sku commands operate on llm_sku with llm_type=dify (unified SKU table).
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.DifySku)
|
||||
cmd := shell.NewResourceCmd(&modules.LLMSku).WithKeyword("dify-sku")
|
||||
cmd.List(new(options.DifySkuListOptions))
|
||||
cmd.Show(new(options.DifySkuShowOptions))
|
||||
cmd.Update(new(options.DifySkuUpdateOptions))
|
||||
@@ -16,5 +17,4 @@ func init() {
|
||||
cmd.Delete(new(options.DifySkuDeleteOptions))
|
||||
cmd.Perform("public", &base_options.BasePublicOptions{})
|
||||
cmd.Perform("private", &base_options.BaseIdOptions{})
|
||||
// cmd.Perform("clone", new(options.DesktopSkuCloneOptions))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/llm"
|
||||
)
|
||||
|
||||
// llm-sku: unified SKU (ollama/vllm/dify). Create/update here for ollama|vllm; for dify use dify-sku or list with --llm-type dify.
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.LLMSku)
|
||||
cmd.List(new(options.LLMSkuListOptions))
|
||||
@@ -16,5 +17,4 @@ func init() {
|
||||
cmd.Delete(new(options.LLMSkuDeleteOptions))
|
||||
cmd.Perform("public", &base_options.BasePublicOptions{})
|
||||
cmd.Perform("private", &base_options.BaseIdOptions{})
|
||||
// cmd.Perform("clone", new(options.DesktopSkuCloneOptions))
|
||||
}
|
||||
|
||||
+3
-2
@@ -76,8 +76,8 @@ type LLMBaseCreateInput struct {
|
||||
type LLMCreateInput struct {
|
||||
LLMBaseCreateInput
|
||||
|
||||
LLMSkuId string
|
||||
LLMImageId string
|
||||
LLMSkuId string `json:"llm_sku_id"`
|
||||
LLMImageId string `json:"llm_image_id"`
|
||||
}
|
||||
|
||||
type LLMBaseListInput struct {
|
||||
@@ -102,6 +102,7 @@ type LLMListInput struct {
|
||||
|
||||
LLMSku string `json:"llm_sku"`
|
||||
LLMImage string `json:"llm_image"`
|
||||
LLMType string `json:"llm_type"` // filter by linked SKU's llm_type (e.g. dify)
|
||||
}
|
||||
|
||||
type ModelInfo struct {
|
||||
|
||||
@@ -10,11 +10,15 @@ type LLMContainerType string
|
||||
|
||||
const (
|
||||
LLM_CONTAINER_OLLAMA LLMContainerType = "ollama"
|
||||
LLM_CONTAINER_VLLM LLMContainerType = "vllm"
|
||||
LLM_CONTAINER_DIFY LLMContainerType = "dify"
|
||||
)
|
||||
|
||||
var (
|
||||
LLM_CONTAINER_TYPES = sets.NewString(
|
||||
string(LLM_CONTAINER_OLLAMA),
|
||||
string(LLM_CONTAINER_VLLM),
|
||||
string(LLM_CONTAINER_DIFY),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package llm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
)
|
||||
|
||||
// LLMSpec is the flat spec for LLM SKU: optional ollama/vllm/dify payload. Type is on LLMSku.LLMType.
|
||||
type LLMSpec struct {
|
||||
Ollama *LLMSpecOllama `json:"ollama,omitempty"`
|
||||
Vllm *LLMSpecVllm `json:"vllm,omitempty"`
|
||||
Dify *LLMSpecDify `json:"dify,omitempty"`
|
||||
}
|
||||
|
||||
func (s *LLMSpec) String() string {
|
||||
return jsonutils.Marshal(s).String()
|
||||
}
|
||||
|
||||
func (s *LLMSpec) IsZero() bool {
|
||||
if s == nil {
|
||||
return true
|
||||
}
|
||||
return s.Ollama == nil && s.Vllm == nil && s.Dify == nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports both new format (type + ollama/vllm/dify) and legacy format (type + data).
|
||||
func (s *LLMSpec) UnmarshalJSON(data []byte) error {
|
||||
var raw struct {
|
||||
Type string `json:"type"`
|
||||
Ollama *LLMSpecOllama `json:"ollama,omitempty"`
|
||||
Vllm *LLMSpecVllm `json:"vllm,omitempty"`
|
||||
Dify *LLMSpecDify `json:"dify,omitempty"`
|
||||
Data json.RawMessage `json:"data,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Ollama = raw.Ollama
|
||||
s.Vllm = raw.Vllm
|
||||
s.Dify = raw.Dify
|
||||
if len(raw.Data) > 0 && s.Ollama == nil && s.Vllm == nil && s.Dify == nil {
|
||||
switch raw.Type {
|
||||
case string(LLM_CONTAINER_OLLAMA):
|
||||
s.Ollama = &LLMSpecOllama{}
|
||||
if err := json.Unmarshal(raw.Data, s.Ollama); err != nil {
|
||||
return err
|
||||
}
|
||||
case string(LLM_CONTAINER_VLLM):
|
||||
s.Vllm = &LLMSpecVllm{}
|
||||
if err := json.Unmarshal(raw.Data, s.Vllm); err != nil {
|
||||
return err
|
||||
}
|
||||
case string(LLM_CONTAINER_DIFY):
|
||||
s.Dify = &LLMSpecDify{}
|
||||
if err := json.Unmarshal(raw.Data, s.Dify); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
s.Ollama = &LLMSpecOllama{}
|
||||
_ = json.Unmarshal(raw.Data, s.Ollama)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LLMSpecOllama holds type-specific fields for ollama SKUs.
|
||||
type LLMSpecOllama struct {
|
||||
LLMImageId string `json:"llm_image_id"`
|
||||
MountedModels []string `json:"mounted_models"`
|
||||
}
|
||||
|
||||
func (s *LLMSpecOllama) String() string {
|
||||
return jsonutils.Marshal(s).String()
|
||||
}
|
||||
|
||||
func (s *LLMSpecOllama) IsZero() bool {
|
||||
if s == nil {
|
||||
return true
|
||||
}
|
||||
return s.LLMImageId == "" && len(s.MountedModels) == 0
|
||||
}
|
||||
|
||||
// LLMSpecVllm holds type-specific fields for vllm SKUs (includes PreferredModel).
|
||||
type LLMSpecVllm struct {
|
||||
LLMImageId string `json:"llm_image_id"`
|
||||
MountedModels []string `json:"mounted_models"`
|
||||
PreferredModel string `json:"preferred_model"`
|
||||
}
|
||||
|
||||
func (s *LLMSpecVllm) String() string {
|
||||
return jsonutils.Marshal(s).String()
|
||||
}
|
||||
|
||||
func (s *LLMSpecVllm) IsZero() bool {
|
||||
if s == nil {
|
||||
return true
|
||||
}
|
||||
return s.LLMImageId == "" && len(s.MountedModels) == 0 && s.PreferredModel == ""
|
||||
}
|
||||
|
||||
// LLMSpecDify holds type-specific fields for Dify SKUs (multiple image ids + customized envs).
|
||||
type LLMSpecDify struct {
|
||||
PostgresImageId string `json:"postgres_image_id"`
|
||||
RedisImageId string `json:"redis_image_id"`
|
||||
NginxImageId string `json:"nginx_image_id"`
|
||||
DifyApiImageId string `json:"dify_api_image_id"`
|
||||
DifyPluginImageId string `json:"dify_plugin_image_id"`
|
||||
DifyWebImageId string `json:"dify_web_image_id"`
|
||||
DifySandboxImageId string `json:"dify_sandbox_image_id"`
|
||||
DifySSRFImageId string `json:"dify_ssrf_image_id"`
|
||||
DifyWeaviateImageId string `json:"dify_weaviate_image_id"`
|
||||
CustomizedEnvs []*DifyCustomizedEnv `json:"customized_envs,omitempty"`
|
||||
}
|
||||
|
||||
func (s *LLMSpecDify) String() string {
|
||||
return jsonutils.Marshal(s).String()
|
||||
}
|
||||
|
||||
func (s *LLMSpecDify) IsZero() bool {
|
||||
if s == nil {
|
||||
return true
|
||||
}
|
||||
return s.PostgresImageId == "" && s.RedisImageId == "" && s.NginxImageId == "" &&
|
||||
s.DifyApiImageId == "" && s.DifyPluginImageId == "" && s.DifyWebImageId == "" &&
|
||||
s.DifySandboxImageId == "" && s.DifySSRFImageId == "" && s.DifyWeaviateImageId == "" &&
|
||||
len(s.CustomizedEnvs) == 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
gotypes.RegisterSerializable(reflect.TypeOf(new(LLMSpec)), func() gotypes.ISerializable {
|
||||
return new(LLMSpec)
|
||||
})
|
||||
}
|
||||
+43
-28
@@ -119,6 +119,11 @@ type LLMSkuDetails struct {
|
||||
MountedModelDetails []MountedModelInfo `json:"mounted_model_details"`
|
||||
|
||||
Template string `json:"template"`
|
||||
|
||||
// LLMType 为 SKU 类型(ollama/vllm/dify),与 llm_spec 一起带出
|
||||
LLMType string `json:"llm_type"`
|
||||
// LLMSpec 从 SKU 持久化字段带出,保证 list/show 与 create 一致
|
||||
LLMSpec *LLMSpec `json:"llm_spec,omitempty"`
|
||||
}
|
||||
|
||||
type MountedAppResourceDetails struct {
|
||||
@@ -172,6 +177,11 @@ type LLMSkuCreateInput struct {
|
||||
|
||||
LLMImageId string `json:"llm_image_id"`
|
||||
LLMType string `json:"llm_type"`
|
||||
|
||||
// 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.
|
||||
// - dify: client must send llm_spec with type "dify" and dify payload.
|
||||
LLMSpec *LLMSpec `json:"llm_spec,omitempty"`
|
||||
}
|
||||
|
||||
type LLMSkuUpdateInput struct {
|
||||
@@ -179,6 +189,11 @@ type LLMSkuUpdateInput struct {
|
||||
MountedModelResourceUpdateInput
|
||||
|
||||
LLMImageId string `json:"llm_image_id"`
|
||||
|
||||
// LLMSpec:
|
||||
// - dify: send full spec to update image ids.
|
||||
// - ollama/vllm: backend may build from llm_image_id/mounted_models; for vllm preferred model should be set in llm_spec.vllm.preferred_model.
|
||||
LLMSpec *LLMSpec `json:"llm_spec,omitempty"`
|
||||
}
|
||||
|
||||
// type LLMModelCloneInput struct {
|
||||
@@ -189,35 +204,35 @@ type LLMSkuUpdateInput struct {
|
||||
// Request bool `json:"request"`
|
||||
// }
|
||||
|
||||
type DifySkulListInput struct {
|
||||
apis.SharableVirtualResourceListInput
|
||||
MountedModelResourceListInput
|
||||
}
|
||||
// type DifySkulListInput struct {
|
||||
// apis.SharableVirtualResourceListInput
|
||||
// MountedModelResourceListInput
|
||||
// }
|
||||
|
||||
type DifySkuCreateInput struct {
|
||||
LLMSKuBaseCreateInput
|
||||
// type DifySkuCreateInput struct {
|
||||
// LLMSKuBaseCreateInput
|
||||
|
||||
PostgresImageId string `json:"postgres_image_id"`
|
||||
RedisImageId string `json:"redis_image_id"`
|
||||
NginxImageId string `json:"nginx_image_id"`
|
||||
DifyApiImageId string `json:"dify_api_image_id"`
|
||||
DifyPluginImageId string `json:"dify_plugin_image_id"`
|
||||
DifyWebImageId string `json:"dify_web_image_id"`
|
||||
DifySandboxImageId string `json:"dify_sandbox_image_id"`
|
||||
DifySSRFImageId string `json:"dify_ssrf_image_id"`
|
||||
DifyWeaviateImageId string `json:"dify_weaviate_image_id"`
|
||||
}
|
||||
// PostgresImageId string `json:"postgres_image_id"`
|
||||
// RedisImageId string `json:"redis_image_id"`
|
||||
// NginxImageId string `json:"nginx_image_id"`
|
||||
// DifyApiImageId string `json:"dify_api_image_id"`
|
||||
// DifyPluginImageId string `json:"dify_plugin_image_id"`
|
||||
// DifyWebImageId string `json:"dify_web_image_id"`
|
||||
// DifySandboxImageId string `json:"dify_sandbox_image_id"`
|
||||
// DifySSRFImageId string `json:"dify_ssrf_image_id"`
|
||||
// DifyWeaviateImageId string `json:"dify_weaviate_image_id"`
|
||||
// }
|
||||
|
||||
type DifySkuUpdateInput struct {
|
||||
LLMSkuBaseUpdateInput
|
||||
// type DifySkuUpdateInput struct {
|
||||
// LLMSkuBaseUpdateInput
|
||||
|
||||
PostgresImageId string `json:"postgres_image_id"`
|
||||
RedisImageId string `json:"redis_image_id"`
|
||||
NginxImageId string `json:"nginx_image_id"`
|
||||
DifyApiImageId string `json:"dify_api_image_id"`
|
||||
DifyPluginImageId string `json:"dify_plugin_image_id"`
|
||||
DifyWebImageId string `json:"dify_web_image_id"`
|
||||
DifySandboxImageId string `json:"dify_sandbox_image_id"`
|
||||
DifySSRFImageId string `json:"dify_ssrf_image_id"`
|
||||
DifyWeaviateImageId string `json:"dify_weaviate_image_id"`
|
||||
}
|
||||
// PostgresImageId string `json:"postgres_image_id"`
|
||||
// RedisImageId string `json:"redis_image_id"`
|
||||
// NginxImageId string `json:"nginx_image_id"`
|
||||
// DifyApiImageId string `json:"dify_api_image_id"`
|
||||
// DifyPluginImageId string `json:"dify_plugin_image_id"`
|
||||
// DifyWebImageId string `json:"dify_web_image_id"`
|
||||
// DifySandboxImageId string `json:"dify_sandbox_image_id"`
|
||||
// DifySSRFImageId string `json:"dify_ssrf_image_id"`
|
||||
// DifyWeaviateImageId string `json:"dify_weaviate_image_id"`
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package llm_container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/llm/models"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
func init() {
|
||||
models.RegisterLLMContainerDriver(newDify())
|
||||
}
|
||||
|
||||
type dify struct{}
|
||||
|
||||
func newDify() models.ILLMContainerDriver {
|
||||
return new(dify)
|
||||
}
|
||||
|
||||
func (d *dify) GetType() api.LLMContainerType {
|
||||
return api.LLM_CONTAINER_DIFY
|
||||
}
|
||||
|
||||
func (d *dify) GetSpec(sku *models.SLLMSku) interface{} {
|
||||
if sku.LLMSpec == nil {
|
||||
return nil
|
||||
}
|
||||
return sku.LLMSpec.Dify
|
||||
}
|
||||
|
||||
func (d *dify) GetPrimaryImageId(sku *models.SLLMSku) string {
|
||||
if spec := d.GetSpec(sku); spec != nil {
|
||||
s := spec.(*api.LLMSpecDify)
|
||||
if s.DifyApiImageId != "" {
|
||||
return s.DifyApiImageId
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *dify) ValidateCreateSpec(ctx context.Context, userCred mcclient.TokenCredential, input *api.LLMSkuCreateInput) (*api.LLMSpec, error) {
|
||||
if input.LLMSpec == nil || input.LLMSpec.Dify == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInputParameter, "dify SKU requires llm_spec with type dify and image ids")
|
||||
}
|
||||
difySpec := input.LLMSpec.Dify
|
||||
for _, imgId := range []*string{&difySpec.PostgresImageId, &difySpec.RedisImageId, &difySpec.NginxImageId, &difySpec.DifyApiImageId, &difySpec.DifyPluginImageId, &difySpec.DifyWebImageId, &difySpec.DifySandboxImageId, &difySpec.DifySSRFImageId, &difySpec.DifyWeaviateImageId} {
|
||||
if *imgId == "" {
|
||||
continue
|
||||
}
|
||||
_, err := validators.ValidateModel(ctx, userCred, models.GetLLMImageManager(), imgId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "validate image_id %s", *imgId)
|
||||
}
|
||||
}
|
||||
return input.LLMSpec, nil
|
||||
}
|
||||
|
||||
func (d *dify) ValidateUpdateSpec(ctx context.Context, userCred mcclient.TokenCredential, sku *models.SLLMSku, input *api.LLMSkuUpdateInput) (*api.LLMSpec, error) {
|
||||
if input.LLMSpec == nil || input.LLMSpec.Dify == nil {
|
||||
return nil, nil
|
||||
}
|
||||
currentSpec := d.GetSpec(sku)
|
||||
if currentSpec == nil {
|
||||
return nil, nil
|
||||
}
|
||||
updated := *currentSpec.(*api.LLMSpecDify)
|
||||
difySpec := input.LLMSpec.Dify
|
||||
mergeStr := func(dst *string, src string) {
|
||||
if src != "" {
|
||||
*dst = src
|
||||
}
|
||||
}
|
||||
mergeStr(&updated.PostgresImageId, difySpec.PostgresImageId)
|
||||
mergeStr(&updated.RedisImageId, difySpec.RedisImageId)
|
||||
mergeStr(&updated.NginxImageId, difySpec.NginxImageId)
|
||||
mergeStr(&updated.DifyApiImageId, difySpec.DifyApiImageId)
|
||||
mergeStr(&updated.DifyPluginImageId, difySpec.DifyPluginImageId)
|
||||
mergeStr(&updated.DifyWebImageId, difySpec.DifyWebImageId)
|
||||
mergeStr(&updated.DifySandboxImageId, difySpec.DifySandboxImageId)
|
||||
mergeStr(&updated.DifySSRFImageId, difySpec.DifySSRFImageId)
|
||||
mergeStr(&updated.DifyWeaviateImageId, difySpec.DifyWeaviateImageId)
|
||||
if len(difySpec.CustomizedEnvs) > 0 {
|
||||
updated.CustomizedEnvs = difySpec.CustomizedEnvs
|
||||
}
|
||||
for _, imgId := range []*string{&updated.PostgresImageId, &updated.RedisImageId, &updated.NginxImageId, &updated.DifyApiImageId, &updated.DifyPluginImageId, &updated.DifyWebImageId, &updated.DifySandboxImageId, &updated.DifySSRFImageId, &updated.DifyWeaviateImageId} {
|
||||
if *imgId != "" {
|
||||
_, err := validators.ValidateModel(ctx, userCred, models.GetLLMImageManager(), imgId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "validate image_id %s", *imgId)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &api.LLMSpec{Ollama: nil, Vllm: nil, Dify: &updated}, nil
|
||||
}
|
||||
|
||||
// GetContainerSpec is required by ILLMContainerDriver but not used for Dify; pod creation uses GetContainerSpecs. Return the first container so the interface is satisfied.
|
||||
func (d *dify) GetContainerSpec(ctx context.Context, llm *models.SLLM, image *models.SLLMImage, sku *models.SLLMSku, props []string, devices []computeapi.SIsolatedDevice, diskId string) *computeapi.PodContainerCreateInput {
|
||||
specs := d.GetContainerSpecs(ctx, llm, image, sku, props, devices, diskId)
|
||||
if len(specs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return specs[0]
|
||||
}
|
||||
|
||||
// GetContainerSpecs returns all Dify pod containers (postgres, redis, api, worker, nginx, etc.). SKU-only policy: customized envs come from llm_spec.dify.customized_envs.
|
||||
func (d *dify) GetContainerSpecs(ctx context.Context, llm *models.SLLM, image *models.SLLMImage, sku *models.SLLMSku, props []string, devices []computeapi.SIsolatedDevice, diskId string) []*computeapi.PodContainerCreateInput {
|
||||
return models.GetDifyContainersByNameAndSku(llm.GetName(), sku, nil)
|
||||
}
|
||||
|
||||
// StartLLM is a no-op for Dify; all services are started by their container entrypoints.
|
||||
func (d *dify) StartLLM(ctx context.Context, userCred mcclient.TokenCredential, llm *models.SLLM) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLLMUrl returns the Dify access URL (nginx port 80). Same pattern as vLLM/Ollama: guest network uses LLMIp, hostlocal uses host IP.
|
||||
func (d *dify) GetLLMUrl(ctx context.Context, userCred mcclient.TokenCredential, llm *models.SLLM) (string, error) {
|
||||
server, err := llm.GetServer(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "get server")
|
||||
}
|
||||
port := 80
|
||||
if p, err := strconv.Atoi(api.DIFY_NGINX_PORT); err == nil {
|
||||
port = p
|
||||
}
|
||||
networkType := llm.NetworkType
|
||||
if networkType == string(computeapi.NETWORK_TYPE_GUEST) {
|
||||
if len(llm.LLMIp) == 0 {
|
||||
return "", errors.Error("LLM IP is empty for guest network")
|
||||
}
|
||||
return fmt.Sprintf("http://%s:%d", llm.LLMIp, port), nil
|
||||
}
|
||||
if len(server.HostAccessIp) == 0 {
|
||||
return "", errors.Error("host access IP is empty")
|
||||
}
|
||||
return fmt.Sprintf("http://%s:%d", server.HostAccessIp, port), nil
|
||||
}
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
commonapi "yunion.io/x/onecloud/pkg/apis"
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/llm/models"
|
||||
llmutil "yunion.io/x/onecloud/pkg/llm/utils"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
@@ -38,6 +40,98 @@ func (o *ollama) GetType() api.LLMContainerType {
|
||||
return api.LLM_CONTAINER_OLLAMA
|
||||
}
|
||||
|
||||
func (o *ollama) GetSpec(sku *models.SLLMSku) interface{} {
|
||||
if sku.LLMType != string(api.LLM_CONTAINER_OLLAMA) || sku.LLMSpec == nil || sku.LLMSpec.Ollama == nil {
|
||||
return nil
|
||||
}
|
||||
return sku.LLMSpec.Ollama
|
||||
}
|
||||
|
||||
func (o *ollama) GetPrimaryImageId(sku *models.SLLMSku) string {
|
||||
if spec := o.GetSpec(sku); spec != nil {
|
||||
return spec.(*api.LLMSpecOllama).LLMImageId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (o *ollama) GetMountedModels(sku *models.SLLMSku) []string {
|
||||
if spec := o.GetSpec(sku); spec != nil {
|
||||
return spec.(*api.LLMSpecOllama).MountedModels
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ollama) ValidateCreateSpec(ctx context.Context, userCred mcclient.TokenCredential, input *api.LLMSkuCreateInput) (*api.LLMSpec, error) {
|
||||
imgObj, err := validators.ValidateModel(ctx, userCred, models.GetLLMImageManager(), &input.LLMImageId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "validate image_id %s", input.LLMImageId)
|
||||
}
|
||||
llmImage := imgObj.(*models.SLLMImage)
|
||||
if llmImage.LLMType != input.LLMType {
|
||||
return nil, errors.Wrapf(httperrors.ErrInvalidStatus, "image %s is not of type %s", input.LLMImageId, input.LLMType)
|
||||
}
|
||||
input.LLMImageId = llmImage.Id
|
||||
if input.MountedModels != nil {
|
||||
for i, mdl := range input.MountedModels {
|
||||
instMdl, err := models.GetInstantModelManager().FetchByIdOrName(ctx, userCred, mdl)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "validate mounted model %s", mdl)
|
||||
}
|
||||
instantModle := instMdl.(*models.SInstantModel)
|
||||
if instantModle.LlmType != input.LLMType {
|
||||
return nil, errors.Wrapf(httperrors.ErrInvalidStatus, "mounted model %s is not of type %s", mdl, input.LLMType)
|
||||
}
|
||||
input.MountedModels[i] = instantModle.GetId()
|
||||
}
|
||||
}
|
||||
return &api.LLMSpec{
|
||||
Ollama: &api.LLMSpecOllama{LLMImageId: input.LLMImageId, MountedModels: input.MountedModels},
|
||||
Vllm: nil,
|
||||
Dify: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *ollama) ValidateUpdateSpec(ctx context.Context, userCred mcclient.TokenCredential, sku *models.SLLMSku, input *api.LLMSkuUpdateInput) (*api.LLMSpec, error) {
|
||||
cur := o.GetSpec(sku)
|
||||
if cur == nil {
|
||||
return nil, nil
|
||||
}
|
||||
curSpec := cur.(*api.LLMSpecOllama)
|
||||
llmImageId := curSpec.LLMImageId
|
||||
mountedModels := curSpec.MountedModels
|
||||
if input.LLMImageId != "" {
|
||||
imgObj, err := validators.ValidateModel(ctx, userCred, models.GetLLMImageManager(), &input.LLMImageId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "validate image_id %s", input.LLMImageId)
|
||||
}
|
||||
llmImage := imgObj.(*models.SLLMImage)
|
||||
if llmImage.LLMType != sku.LLMType {
|
||||
return nil, errors.Wrapf(httperrors.ErrInvalidStatus, "image %s is not of type %s", input.LLMImageId, sku.LLMType)
|
||||
}
|
||||
llmImageId = llmImage.Id
|
||||
}
|
||||
if input.MountedModels != nil {
|
||||
mountedModels = make([]string, len(input.MountedModels))
|
||||
for i, mdl := range input.MountedModels {
|
||||
instMdl, err := models.GetInstantModelManager().FetchByIdOrName(ctx, userCred, mdl)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "validate mounted model %s", mdl)
|
||||
}
|
||||
instantModle := instMdl.(*models.SInstantModel)
|
||||
if instantModle.LlmType != sku.LLMType {
|
||||
return nil, errors.Wrapf(httperrors.ErrInvalidStatus, "mounted model %s is not of type %s", mdl, sku.LLMType)
|
||||
}
|
||||
mountedModels[i] = instantModle.GetId()
|
||||
}
|
||||
}
|
||||
input.MountedModels = mountedModels
|
||||
return &api.LLMSpec{
|
||||
Ollama: &api.LLMSpecOllama{LLMImageId: llmImageId, MountedModels: mountedModels},
|
||||
Vllm: nil,
|
||||
Dify: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *ollama) GetContainerSpec(ctx context.Context, llm *models.SLLM, image *models.SLLMImage, sku *models.SLLMSku, props []string, devices []computeapi.SIsolatedDevice, diskId string) *computeapi.PodContainerCreateInput {
|
||||
spec := computeapi.ContainerSpec{
|
||||
ContainerSpec: commonapi.ContainerSpec{
|
||||
@@ -464,6 +558,10 @@ func (o *ollama) CheckDuplicateMounts(errStr string, dupIndex int) string {
|
||||
return fmt.Sprintf("Model %s and %s have duplicated container target dirs", firstModel, secondModel)
|
||||
}
|
||||
|
||||
func (o *ollama) StartLLM(ctx context.Context, userCred mcclient.TokenCredential, llm *models.SLLM) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// func download(ctx context.Context, userCred mcclient.TokenCredential, containerId string, taskId string, webUrl string, path string) error {
|
||||
// input := &computeapi.ContainerDownloadFileInput{
|
||||
// WebUrl: webUrl,
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
commonapi "yunion.io/x/onecloud/pkg/apis"
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
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/modules/compute"
|
||||
)
|
||||
|
||||
var difyManager *SDifyManager
|
||||
|
||||
func init() {
|
||||
GetDifyManager()
|
||||
}
|
||||
|
||||
func GetDifyManager() *SDifyManager {
|
||||
if difyManager != nil {
|
||||
return difyManager
|
||||
}
|
||||
difyManager = &SDifyManager{
|
||||
SLLMBaseManager: NewSLLMBaseManager(
|
||||
SDify{},
|
||||
"difies_tbl",
|
||||
"dify",
|
||||
"difies",
|
||||
),
|
||||
}
|
||||
difyManager.SetVirtualObject(difyManager)
|
||||
return difyManager
|
||||
}
|
||||
|
||||
type SDifyManager struct {
|
||||
SLLMBaseManager
|
||||
}
|
||||
|
||||
type SDify struct {
|
||||
SLLMBase
|
||||
|
||||
DifySkuId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required"`
|
||||
}
|
||||
|
||||
func (dm *SDifyManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.DifyCreateInput) (*api.DifyCreateInput, error) {
|
||||
var err error
|
||||
input.LLMBaseCreateInput, err = dm.SLLMBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.LLMBaseCreateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validate VirtualResourceCreateInput")
|
||||
}
|
||||
sku, err := GetDifySkuManager().FetchByIdOrName(ctx, userCred, input.DifySkuId)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "fetch DifySku")
|
||||
}
|
||||
dSku := sku.(*SDifySku)
|
||||
input.DifySkuId = dSku.Id
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (dm *SDifyManager) OnCreateComplete(ctx context.Context, items []db.IModel, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data []jsonutils.JSONObject) {
|
||||
parentTaskId, _ := data[0].GetString("parent_task_id")
|
||||
err := runBatchCreateTask(ctx, items, userCred, data, "DifyBatchCreateTask", parentTaskId)
|
||||
if err != nil {
|
||||
for i := range items {
|
||||
llm := items[i].(*SDify)
|
||||
llm.SetStatus(ctx, userCred, api.LLM_STATUS_CREATE_FAIL, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dm *SDifyManager) BatchCreateValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.DifyCreateInput) (*jsonutils.JSONDict, error) {
|
||||
data, err := dm.ValidateCreateData(ctx, userCred, ownerId, query, &input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data.JSON(data), nil
|
||||
}
|
||||
|
||||
func (dm *SDifyManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.DifyListInput) (*sqlchemy.SQuery, error) {
|
||||
q, err := dm.SLLMBaseManager.ListItemFilter(ctx, q, userCred, input.LLMBaseListInput)
|
||||
if err != nil {
|
||||
return q, errors.Wrap(err, "VirtualResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
if len(input.DifySku) > 0 {
|
||||
skuObj, err := GetDifySkuManager().FetchByIdOrName(ctx, userCred, input.DifySku)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, httperrors.NewResourceNotFoundError2(GetDifySkuManager().KeywordPlural(), input.DifySku)
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "GetDifySkuManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
q = q.Equals("dify_sku_id", skuObj.GetId())
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (dify *SDify) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
return dify.StartDeleteTask(ctx, userCred, "")
|
||||
}
|
||||
|
||||
func (dify *SDify) GetDifySku(skuId string) (*SDifySku, error) {
|
||||
if len(skuId) == 0 {
|
||||
skuId = dify.DifySkuId
|
||||
}
|
||||
sku, err := GetDifySkuManager().FetchById(skuId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetch DifySku")
|
||||
}
|
||||
return sku.(*SDifySku), nil
|
||||
}
|
||||
|
||||
func (dify *SDify) GetDifyContainers() []*computeapi.PodContainerCreateInput {
|
||||
keys := []string{
|
||||
api.DIFY_POSTGRES_KEY,
|
||||
api.DIFY_REDIS_KEY,
|
||||
api.DIFY_API_KEY,
|
||||
api.DIFY_WORKER_KEY,
|
||||
api.DIFY_WORKER_BEAT_KEY,
|
||||
api.DIFY_PLUGIN_KEY,
|
||||
api.DIFY_SANDBOX_KEY,
|
||||
api.DIFY_SSRF_KEY,
|
||||
api.DIFY_WEB_KEY,
|
||||
api.DIFY_NGINX_KEY,
|
||||
api.DIFY_WEAVIATE_KEY,
|
||||
}
|
||||
|
||||
var containers []*computeapi.PodContainerCreateInput
|
||||
for _, key := range keys {
|
||||
if c, err := dify.getDifyContainerByContainerKey(key); err == nil {
|
||||
containers = append(containers, c)
|
||||
}
|
||||
}
|
||||
return containers
|
||||
}
|
||||
|
||||
func (dify *SDify) StartCreateTask(ctx context.Context, userCred mcclient.TokenCredential, input api.DifyCreateInput, parentTaskId string) error {
|
||||
dify.SetStatus(ctx, userCred, commonapi.STATUS_CREATING, "")
|
||||
params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
|
||||
var err = func() error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "DifyCreateTask", dify, userCred, params, parentTaskId, "", nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "NewTask")
|
||||
}
|
||||
return task.ScheduleRun(params)
|
||||
}()
|
||||
if err != nil {
|
||||
dify.SetStatus(ctx, userCred, api.LLM_STATUS_CREATE_FAIL, err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dify *SDify) StartDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
||||
dify.SetStatus(ctx, userCred, api.LLM_STATUS_START_DELETE, "StartDeleteTask")
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "DifyDeleteTask", dify, userCred, nil, parentTaskId, "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return task.ScheduleRun(nil)
|
||||
}
|
||||
|
||||
func (dify *SDify) ServerCreate(ctx context.Context, userCred mcclient.TokenCredential, s *mcclient.ClientSession, input *api.DifyCreateInput) (string, error) {
|
||||
sku, err := dify.GetDifySku(dify.DifySkuId)
|
||||
if nil != err {
|
||||
return "", errors.Wrap(err, "GetDifySku")
|
||||
}
|
||||
|
||||
data, err := GetDifyPodCreateInput(ctx, userCred, input, dify, sku, "")
|
||||
if nil != err {
|
||||
return "", errors.Wrap(err, "GetDifyPodCreateInput")
|
||||
}
|
||||
log.Infoln("PodCreateInput Data: ", jsonutils.Marshal(data).String())
|
||||
|
||||
resp, err := compute.Servers.Create(s, jsonutils.Marshal(data))
|
||||
if nil != err {
|
||||
return "", errors.Wrap(err, "Servers.Create")
|
||||
}
|
||||
id, err := resp.GetString("id")
|
||||
if nil != err {
|
||||
return "", errors.Wrap(err, "resp.GetString")
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// func (llm *SDify) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
|
||||
// instanceId, isBound, err := llm.IsBoundToInstance()
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "IsBoundToInstance")
|
||||
// }
|
||||
// if isBound {
|
||||
// return httperrors.NewBadRequestError("llm is bound to instance %s", instanceId)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (dify *SDify) getDifyContainerByContainerKey(containerKey string) (*computeapi.PodContainerCreateInput, error) {
|
||||
sku, err := dify.GetDifySku("")
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
container, err := getDifyContainersManager().GetContainer(dify.GetName(), containerKey, sku)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
container.AlwaysRestart = true // always restart to solve dependency issue
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (dify *SDify) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
// can't start while it's already running
|
||||
if utils.IsInStringArray(dify.Status, computeapi.VM_RUNNING_STATUS) {
|
||||
return nil, errors.Wrapf(errors.ErrInvalidStatus, "dify id: %s status: %s", dify.Id, dify.Status)
|
||||
}
|
||||
|
||||
if err := dify.StartStartTask(ctx, userCred, ""); err != nil {
|
||||
return nil, errors.Wrap(err, "StartStartTask")
|
||||
}
|
||||
return jsonutils.Marshal(nil), nil
|
||||
}
|
||||
|
||||
func (dify *SDify) StartStartTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "DifyStartTask", dify, userCred, nil, parentTaskId, "", nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewTask")
|
||||
}
|
||||
return task.ScheduleRun(nil)
|
||||
}
|
||||
|
||||
func (dify *SDify) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
if dify.Status == computeapi.VM_READY {
|
||||
return nil, errors.Wrapf(errors.ErrInvalidStatus, "dify id: %s status: %s", dify.Id, dify.Status)
|
||||
}
|
||||
dify.SetStatus(ctx, userCred, computeapi.VM_START_STOP, "perform stop")
|
||||
err := dify.StartDifyStopTask(ctx, userCred, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "StartStopTask")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (dify *SDify) StartDifyStopTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "DifyStopTask", dify, userCred, nil, parentTaskId, "", nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewTask")
|
||||
}
|
||||
err = task.ScheduleRun(nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ScheduleRun")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// func (dify *SDify) ContainerCreate(ctx context.Context, userCred mcclient.TokenCredential, containerKey string) (string, error) {
|
||||
// model, err := dify.GetDifyModel("")
|
||||
// if nil != err {
|
||||
// return "", errors.Wrap(err, "GetDifyModel")
|
||||
// }
|
||||
// // get input
|
||||
// input, err := getDifyContainersManager().GetContainer(dify.GetName(), containerKey, model)
|
||||
// if nil != err {
|
||||
// return "", errors.Wrap(err, "GetContainer")
|
||||
// }
|
||||
|
||||
// // create on pod
|
||||
// params := &computeapi.ContainerCreateInput{
|
||||
// Spec: input.ContainerSpec,
|
||||
// }
|
||||
// s := auth.GetSession(ctx, userCred, "")
|
||||
|
||||
// return "", nil
|
||||
// }
|
||||
|
||||
func getDifyContainersManager() *DifyContainersManager {
|
||||
return &DifyContainersManager{}
|
||||
}
|
||||
@@ -48,6 +48,23 @@ func (envsPtr *DifyContainerEnv) SetContainerEnv(key, value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DifyCustomizedEnvsToMap converts API DifyCustomizedEnvs to DifyContainerEnv for container build. Returns nil if empty.
|
||||
func DifyCustomizedEnvsToMap(envs []*api.DifyCustomizedEnv) DifyContainerEnv {
|
||||
if len(envs) == 0 {
|
||||
return nil
|
||||
}
|
||||
m := make(DifyContainerEnv, len(envs))
|
||||
for _, e := range envs {
|
||||
if e != nil && e.Key != "" {
|
||||
m[e.Key] = e.Value
|
||||
}
|
||||
}
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func getPVCMount(name, subDir, mountPath string) *apis.ContainerVolumeMount {
|
||||
diskIndex := 0
|
||||
pvc := &apis.ContainerVolumeMount{
|
||||
@@ -65,10 +82,6 @@ func getPVCMount(name, subDir, mountPath string) *apis.ContainerVolumeMount {
|
||||
return pvc
|
||||
}
|
||||
|
||||
type DifyContainersManager struct {
|
||||
UserCustomizedEnvs *DifyContainerEnv
|
||||
}
|
||||
|
||||
func _getRegistryImage(imageId string) string {
|
||||
image, err := GetLLMImageManager().FetchById(imageId)
|
||||
if err != nil {
|
||||
@@ -77,36 +90,109 @@ func _getRegistryImage(imageId string) string {
|
||||
return image.(*SLLMImage).ToContainerImage()
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) GetContainer(name, containerKey string, sku *SDifySku) (*computeapi.PodContainerCreateInput, error) {
|
||||
switch containerKey {
|
||||
// getDifySpecFromSku returns the Dify spec from the SKU via the container driver, or nil.
|
||||
func getDifySpecFromSku(sku *SLLMSku) *api.LLMSpecDify {
|
||||
s := sku.GetLLMContainerDriver().GetSpec(sku)
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
d, _ := s.(*api.LLMSpecDify)
|
||||
return d
|
||||
}
|
||||
|
||||
func getDifyContainerByNameKeyAndSku(name, key string, sku *SLLMSku, customEnvs *DifyContainerEnv) (*computeapi.PodContainerCreateInput, error) {
|
||||
spec := getDifySpecFromSku(sku)
|
||||
if spec == nil {
|
||||
return nil, errors.New("sku is not a Dify SKU or LLMSpec is missing")
|
||||
}
|
||||
switch key {
|
||||
case api.DIFY_REDIS_KEY:
|
||||
return m._getRedisContainer(name, containerKey, _getRegistryImage(sku.RedisImageId)), nil
|
||||
return getRedisContainer(name, key, _getRegistryImage(spec.RedisImageId), customEnvs), nil
|
||||
case api.DIFY_POSTGRES_KEY:
|
||||
return m._getPostgresContainer(name, containerKey, _getRegistryImage(sku.PostgresImageId)), nil
|
||||
return getPostgresContainer(name, key, _getRegistryImage(spec.PostgresImageId), customEnvs), nil
|
||||
case api.DIFY_API_KEY:
|
||||
return m._getApiContainer(name, containerKey, _getRegistryImage(sku.DifyApiImageId)), nil
|
||||
return getApiContainer(name, key, _getRegistryImage(spec.DifyApiImageId), customEnvs), nil
|
||||
case api.DIFY_WORKER_KEY:
|
||||
return m._getWorkerContainer(name, containerKey, _getRegistryImage(sku.DifyApiImageId)), nil
|
||||
return getWorkerContainer(name, key, _getRegistryImage(spec.DifyApiImageId), customEnvs), nil
|
||||
case api.DIFY_WORKER_BEAT_KEY:
|
||||
return m._getWorkerBeatContainer(name, containerKey, _getRegistryImage(sku.DifyApiImageId)), nil
|
||||
return getWorkerBeatContainer(name, key, _getRegistryImage(spec.DifyApiImageId), customEnvs), nil
|
||||
case api.DIFY_PLUGIN_KEY:
|
||||
return m._getPluginContainer(name, containerKey, _getRegistryImage(sku.DifyPluginImageId)), nil
|
||||
return getPluginContainer(name, key, _getRegistryImage(spec.DifyPluginImageId), customEnvs), nil
|
||||
case api.DIFY_WEB_KEY:
|
||||
return m._getWebContainer(name, containerKey, _getRegistryImage(sku.DifyWebImageId)), nil
|
||||
return getWebContainer(name, key, _getRegistryImage(spec.DifyWebImageId), customEnvs), nil
|
||||
case api.DIFY_SSRF_KEY:
|
||||
return m._getSsrfContainer(name, containerKey, _getRegistryImage(sku.DifySSRFImageId)), nil
|
||||
return getSsrfContainer(name, key, _getRegistryImage(spec.DifySSRFImageId), customEnvs), nil
|
||||
case api.DIFY_NGINX_KEY:
|
||||
return m._getNginxContainer(name, containerKey, _getRegistryImage(sku.NginxImageId)), nil
|
||||
return getNginxContainer(name, key, _getRegistryImage(spec.NginxImageId), customEnvs), nil
|
||||
case api.DIFY_WEAVIATE_KEY:
|
||||
return m._getWeaviateContainer(name, containerKey, _getRegistryImage(sku.DifyWeaviateImageId)), nil
|
||||
return getWeaviateContainer(name, key, _getRegistryImage(spec.DifyWeaviateImageId), customEnvs), nil
|
||||
case api.DIFY_SANDBOX_KEY:
|
||||
return m._getSandboxContainer(name, containerKey, _getRegistryImage(sku.DifySandboxImageId)), nil
|
||||
return getSandboxContainer(name, key, _getRegistryImage(spec.DifySandboxImageId), customEnvs), nil
|
||||
default:
|
||||
return nil, errors.New("unsupported container key")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getRedisContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
// DifyContainerKeys is the ordered list of container keys for a Dify pod (used by driver and SDify).
|
||||
var DifyContainerKeys = []string{
|
||||
api.DIFY_POSTGRES_KEY,
|
||||
api.DIFY_REDIS_KEY,
|
||||
api.DIFY_API_KEY,
|
||||
api.DIFY_WORKER_KEY,
|
||||
api.DIFY_WORKER_BEAT_KEY,
|
||||
api.DIFY_PLUGIN_KEY,
|
||||
api.DIFY_SANDBOX_KEY,
|
||||
api.DIFY_SSRF_KEY,
|
||||
api.DIFY_WEB_KEY,
|
||||
api.DIFY_NGINX_KEY,
|
||||
api.DIFY_WEAVIATE_KEY,
|
||||
}
|
||||
|
||||
// mergeDifyContainerEnvs merges base and overrides; overrides take precedence. Returns nil if both are nil/empty.
|
||||
func mergeDifyContainerEnvs(base, overrides *DifyContainerEnv) *DifyContainerEnv {
|
||||
if base == nil && overrides == nil {
|
||||
return nil
|
||||
}
|
||||
out := make(DifyContainerEnv)
|
||||
if base != nil {
|
||||
for k, v := range *base {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
if overrides != nil {
|
||||
for k, v := range *overrides {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &out
|
||||
}
|
||||
|
||||
func GetDifyContainersByNameAndSku(name string, sku *SLLMSku, customEnvs *DifyContainerEnv) []*computeapi.PodContainerCreateInput {
|
||||
var skuEnvs *DifyContainerEnv
|
||||
if d := getDifySpecFromSku(sku); d != nil && len(d.CustomizedEnvs) > 0 {
|
||||
m := DifyCustomizedEnvsToMap(d.CustomizedEnvs)
|
||||
if m != nil {
|
||||
skuEnvs = &m
|
||||
}
|
||||
}
|
||||
mergedEnvs := mergeDifyContainerEnvs(skuEnvs, customEnvs)
|
||||
|
||||
var out []*computeapi.PodContainerCreateInput
|
||||
for _, key := range DifyContainerKeys {
|
||||
c, err := getDifyContainerByNameKeyAndSku(name, key, sku, mergedEnvs)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
c.AlwaysRestart = true
|
||||
out = append(out, c)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func getRedisContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -117,7 +203,7 @@ func (m *DifyContainersManager) _getRedisContainer(name, key, image string) *com
|
||||
envs := &DifyContainerEnv{
|
||||
"REDISCLI_AUTH": api.DIFY_REDISCLI_AUTH,
|
||||
}
|
||||
ctr.Envs = envs.GetContainerEnvs(m.UserCustomizedEnvs)
|
||||
ctr.Envs = envs.GetContainerEnvs(customEnvs)
|
||||
|
||||
// set PVC to store data
|
||||
ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
@@ -132,7 +218,7 @@ func (m *DifyContainersManager) _getRedisContainer(name, key, image string) *com
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getPostgresContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getPostgresContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -146,7 +232,7 @@ func (m *DifyContainersManager) _getPostgresContainer(name, key, image string) *
|
||||
"POSTGRES_DB": api.DIFY_POSTGRES_DB,
|
||||
"PGDATA": path.Join(api.DIFY_POSTGRES_PVC_MOUNT_PATH, api.DIFY_POSTGRES_PGDATA),
|
||||
}
|
||||
ctr.Envs = envs.GetContainerEnvs(m.UserCustomizedEnvs)
|
||||
ctr.Envs = envs.GetContainerEnvs(customEnvs)
|
||||
|
||||
// set PVC to store data
|
||||
ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
@@ -166,7 +252,7 @@ func (m *DifyContainersManager) _getPostgresContainer(name, key, image string) *
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getApiContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getApiContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -190,7 +276,7 @@ func (m *DifyContainersManager) _getApiContainer(name, key, image string) *compu
|
||||
"PLUGIN_MAX_PACKAGE_SIZE": api.DIFY_PLUGIN_MAX_PACKAGE_SIZE,
|
||||
"INNER_API_KEY_FOR_PLUGIN": api.DIFY_API_INNER_KEY,
|
||||
}
|
||||
ctr.Envs = append(getSharedApiWorkerEnv(m.UserCustomizedEnvs), envs.GetContainerEnvs(m.UserCustomizedEnvs)...)
|
||||
ctr.Envs = append(getSharedApiWorkerEnv(customEnvs), envs.GetContainerEnvs(customEnvs)...)
|
||||
|
||||
// set PVC to store data
|
||||
ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
@@ -200,7 +286,7 @@ func (m *DifyContainersManager) _getApiContainer(name, key, image string) *compu
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getWorkerContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getWorkerContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -222,7 +308,7 @@ func (m *DifyContainersManager) _getWorkerContainer(name, key, image string) *co
|
||||
"PLUGIN_MAX_PACKAGE_SIZE": api.DIFY_PLUGIN_MAX_PACKAGE_SIZE,
|
||||
"INNER_API_KEY_FOR_PLUGIN": api.DIFY_API_INNER_KEY,
|
||||
}
|
||||
ctr.Envs = append(getSharedApiWorkerEnv(m.UserCustomizedEnvs), envs.GetContainerEnvs(m.UserCustomizedEnvs)...)
|
||||
ctr.Envs = append(getSharedApiWorkerEnv(customEnvs), envs.GetContainerEnvs(customEnvs)...)
|
||||
|
||||
// set PVC to store data
|
||||
ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
@@ -232,7 +318,7 @@ func (m *DifyContainersManager) _getWorkerContainer(name, key, image string) *co
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getWorkerBeatContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getWorkerBeatContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -249,12 +335,12 @@ func (m *DifyContainersManager) _getWorkerBeatContainer(name, key, image string)
|
||||
envs := &DifyContainerEnv{
|
||||
"MODE": api.DIFY_WORKER_BEAT_MODE,
|
||||
}
|
||||
ctr.Envs = append(getSharedApiWorkerEnv(m.UserCustomizedEnvs), envs.GetContainerEnvs(m.UserCustomizedEnvs)...)
|
||||
ctr.Envs = append(getSharedApiWorkerEnv(customEnvs), envs.GetContainerEnvs(customEnvs)...)
|
||||
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getPluginContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getPluginContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -288,7 +374,7 @@ func (m *DifyContainersManager) _getPluginContainer(name, key, image string) *co
|
||||
"PLUGIN_PACKAGE_CACHE_PATH": api.DIFY_PLUGIN_PACKAGE_CACHE_PATH,
|
||||
"PLUGIN_MEDIA_CACHE_PATH": api.DIFY_PLUGIN_MEDIA_CACHE_PATH,
|
||||
}
|
||||
ctr.Envs = append(getSharedApiWorkerEnv(m.UserCustomizedEnvs), envs.GetContainerEnvs(m.UserCustomizedEnvs)...)
|
||||
ctr.Envs = append(getSharedApiWorkerEnv(customEnvs), envs.GetContainerEnvs(customEnvs)...)
|
||||
|
||||
// set PVC to store data
|
||||
ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
@@ -298,7 +384,7 @@ func (m *DifyContainersManager) _getPluginContainer(name, key, image string) *co
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getWebContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getWebContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -329,12 +415,12 @@ func (m *DifyContainersManager) _getWebContainer(name, key, image string) *compu
|
||||
"ENABLE_WEBSITE_FIRECRAWL": api.DIFY_WEB_ENABLE_WEBSITE_FIRECRAWL,
|
||||
"ENABLE_WEBSITE_WATERCRAWL": api.DIFY_WEB_ENABLE_WEBSITE_WATERCRAWL,
|
||||
}
|
||||
ctr.Envs = envs.GetContainerEnvs(m.UserCustomizedEnvs)
|
||||
ctr.Envs = envs.GetContainerEnvs(customEnvs)
|
||||
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getSsrfContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getSsrfContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -350,7 +436,7 @@ func (m *DifyContainersManager) _getSsrfContainer(name, key, image string) *comp
|
||||
"SANDBOX_HOST": api.DIFY_LOCALHOST,
|
||||
"SANDBOX_PORT": api.DIFY_SANDBOX_PORT,
|
||||
}
|
||||
ctr.Envs = envs.GetContainerEnvs(m.UserCustomizedEnvs)
|
||||
ctr.Envs = envs.GetContainerEnvs(customEnvs)
|
||||
|
||||
// set PVC to store data
|
||||
// ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
@@ -366,7 +452,7 @@ func (m *DifyContainersManager) _getSsrfContainer(name, key, image string) *comp
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getNginxContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getNginxContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -389,7 +475,7 @@ func (m *DifyContainersManager) _getNginxContainer(name, key, image string) *com
|
||||
"NGINX_PROXY_READ_TIMEOUT": api.DIFY_NGINX_PROXY_READ_TIMEOUT,
|
||||
"NGINX_PROXY_SEND_TIMEOUT": api.DIFY_NGINX_PROXY_SEND_TIMEOUT,
|
||||
}
|
||||
ctr.Envs = envs.GetContainerEnvs(m.UserCustomizedEnvs)
|
||||
ctr.Envs = envs.GetContainerEnvs(customEnvs)
|
||||
|
||||
// set PVC to store data
|
||||
ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
@@ -405,7 +491,7 @@ func (m *DifyContainersManager) _getNginxContainer(name, key, image string) *com
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getWeaviateContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getWeaviateContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -425,7 +511,7 @@ func (m *DifyContainersManager) _getWeaviateContainer(name, key, image string) *
|
||||
"AUTHORIZATION_ADMINLIST_ENABLED": api.DIFY_WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED,
|
||||
"AUTHORIZATION_ADMINLIST_USERS": api.DIFY_WEAVIATE_AUTHORIZATION_ADMINLIST_USERS,
|
||||
}
|
||||
ctr.Envs = envs.GetContainerEnvs(m.UserCustomizedEnvs)
|
||||
ctr.Envs = envs.GetContainerEnvs(customEnvs)
|
||||
|
||||
// set PVC to store data
|
||||
ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
@@ -435,7 +521,7 @@ func (m *DifyContainersManager) _getWeaviateContainer(name, key, image string) *
|
||||
return ctr
|
||||
}
|
||||
|
||||
func (m *DifyContainersManager) _getSandboxContainer(name, key, image string) *computeapi.PodContainerCreateInput {
|
||||
func getSandboxContainer(name, key, image string, customEnvs *DifyContainerEnv) *computeapi.PodContainerCreateInput {
|
||||
// set name and image
|
||||
ctr := &computeapi.PodContainerCreateInput{
|
||||
Name: name + "-" + key,
|
||||
@@ -453,7 +539,7 @@ func (m *DifyContainersManager) _getSandboxContainer(name, key, image string) *c
|
||||
"SANDBOX_PORT": api.DIFY_SANDBOX_PORT,
|
||||
"PIP_MIRROR_URL": api.PIP_MIRROR_URL,
|
||||
}
|
||||
ctr.Envs = envs.GetContainerEnvs(m.UserCustomizedEnvs)
|
||||
ctr.Envs = envs.GetContainerEnvs(customEnvs)
|
||||
|
||||
// set PVC to store data
|
||||
ctr.VolumeMounts = []*apis.ContainerVolumeMount{
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
func GetDifyPodCreateInput(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
input *api.DifyCreateInput,
|
||||
dify *SDify,
|
||||
sku *SDifySku,
|
||||
eip string,
|
||||
) (*computeapi.ServerCreateInput, error) {
|
||||
data, err := GetLLMBasePodCreateInput(ctx, userCred, &input.LLMBaseCreateInput, &dify.SLLMBase, &sku.SLLMSkuBase, eip)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetLLMBasePodCreateInput: ")
|
||||
}
|
||||
|
||||
ctrs := dify.GetDifyContainers()
|
||||
|
||||
data.Pod = &computeapi.PodCreateInput{
|
||||
HostIPC: true,
|
||||
Containers: ctrs,
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
func init() {
|
||||
GetDifySkuManager()
|
||||
}
|
||||
|
||||
var difySkuManager *SDifySkuManager
|
||||
|
||||
func GetDifySkuManager() *SDifySkuManager {
|
||||
if difySkuManager != nil {
|
||||
return difySkuManager
|
||||
}
|
||||
difySkuManager = &SDifySkuManager{
|
||||
SLLMSkuBaseManager: NewSLLMSkuBaseManager(
|
||||
SDifySku{},
|
||||
"dify_skus_tbl",
|
||||
"dify_sku",
|
||||
"dify_skus",
|
||||
),
|
||||
}
|
||||
difySkuManager.SetVirtualObject(difySkuManager)
|
||||
return difySkuManager
|
||||
}
|
||||
|
||||
type SDifySkuManager struct {
|
||||
SLLMSkuBaseManager
|
||||
}
|
||||
|
||||
type SDifySku struct {
|
||||
SLLMSkuBase
|
||||
|
||||
PostgresImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
RedisImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
NginxImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
DifyApiImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
DifyPluginImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
DifyWebImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
DifySandboxImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
DifySSRFImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
DifyWeaviateImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
}
|
||||
|
||||
func (man *SDifySkuManager) ListItemFilter(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
input api.DifySkulListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = man.SLLMSkuBaseManager.ListItemFilter(ctx, q, userCred, input.SharableVirtualResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SLLMSkuBaseManager.ListItemFilter")
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
// func (man *SDifyModelManager) FetchCustomizeColumns(
|
||||
// ctx context.Context,
|
||||
// userCred mcclient.TokenCredential,
|
||||
// query jsonutils.JSONObject,
|
||||
// objs []interface{},
|
||||
// fields stringutils2.SSortedStrings,
|
||||
// isList bool,
|
||||
// ) []api.LLMModelDetails {
|
||||
|
||||
// }
|
||||
|
||||
func (man *SDifySkuManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.DifySkuCreateInput) (*api.DifySkuCreateInput, error) {
|
||||
var err error
|
||||
input.LLMSKuBaseCreateInput, err = man.SLLMSkuBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.LLMSKuBaseCreateInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SLLMSkuBaseManager.ValidateCreateData")
|
||||
}
|
||||
|
||||
for _, imgId := range []*string{&input.PostgresImageId, &input.RedisImageId, &input.NginxImageId, &input.DifyApiImageId, &input.DifyPluginImageId, &input.DifyWebImageId, &input.DifySandboxImageId, &input.DifySSRFImageId, &input.DifyWeaviateImageId} {
|
||||
_, err := validators.ValidateModel(ctx, userCred, GetLLMImageManager(), imgId)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "validate image_id %s", *imgId)
|
||||
}
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (sku *SDifySku) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DifySkuUpdateInput) (api.DifySkuUpdateInput, error) {
|
||||
var err error
|
||||
input.LLMSkuBaseUpdateInput, err = sku.SLLMSkuBase.ValidateUpdateData(ctx, userCred, query, input.LLMSkuBaseUpdateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validate LLMSkuBaseUpdateInput")
|
||||
}
|
||||
|
||||
for _, imgId := range []*string{&input.PostgresImageId, &input.RedisImageId, &input.NginxImageId, &input.DifyApiImageId, &input.DifyPluginImageId, &input.DifyWebImageId, &input.DifySandboxImageId, &input.DifySSRFImageId, &input.DifyWeaviateImageId} {
|
||||
if *imgId != "" {
|
||||
_, err := validators.ValidateModel(ctx, userCred, GetLLMImageManager(), imgId)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "validate image_id %s", *imgId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (sku *SDifySku) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
|
||||
count, err := GetDifyManager().Query().Equals("dify_sku_id", sku.Id).CountWithError()
|
||||
if nil != err {
|
||||
return errors.Wrap(err, "fetch dify")
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.Wrap(errors.ErrNotSupported, "This sku is currently in use")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -296,8 +296,8 @@ func (man *SInstantModelManager) FetchCustomizeColumns(
|
||||
return res
|
||||
}
|
||||
|
||||
func (man *SInstantModelManager) GetLLMContainerDriver(llmType apis.LLMContainerType) ILLMContainerDriver {
|
||||
return GetLLMContainerDriver(llmType)
|
||||
func (man *SInstantModelManager) GetLLMContainerInstantModelDriver(llmType apis.LLMContainerType) (ILLMContainerInstantModelDriver, error) {
|
||||
return GetLLMContainerInstantModelDriver(llmType)
|
||||
}
|
||||
|
||||
func (man *SInstantModelManager) ValidateCreateData(
|
||||
@@ -342,7 +342,10 @@ func (man *SInstantModelManager) ValidateCreateData(
|
||||
input.ActualSizeMb = img.MinDiskMB
|
||||
}
|
||||
if len(input.Mounts) > 0 {
|
||||
drv := man.GetLLMContainerDriver(input.LlmType)
|
||||
drv, err := man.GetLLMContainerInstantModelDriver(input.LlmType)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "GetLLMContainerInstantModelDriver")
|
||||
}
|
||||
_, err = drv.ValidateMounts(input.Mounts, input.ModelName, input.ModelTag)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validateMounts")
|
||||
@@ -389,7 +392,10 @@ func (model *SInstantModel) ValidateUpdateData(
|
||||
input.ActualSizeMb = img.MinDiskMB
|
||||
}
|
||||
if len(input.Mounts) > 0 {
|
||||
drv := GetInstantModelManager().GetLLMContainerDriver(apis.LLMContainerType(model.LlmType))
|
||||
drv, err := GetInstantModelManager().GetLLMContainerInstantModelDriver(apis.LLMContainerType(model.LlmType))
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "GetLLMContainerInstantModelDriver")
|
||||
}
|
||||
input.Mounts, err = drv.ValidateMounts(input.Mounts, model.ModelName, model.ModelTag)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validateMounts")
|
||||
@@ -433,7 +439,11 @@ func (model *SInstantModel) PostUpdate(
|
||||
}
|
||||
|
||||
func (model *SInstantModel) getImagePaths() map[string]string {
|
||||
drv := GetInstantModelManager().GetLLMContainerDriver(apis.LLMContainerType(model.LlmType))
|
||||
drv, err := GetInstantModelManager().GetLLMContainerInstantModelDriver(apis.LLMContainerType(model.LlmType))
|
||||
if err != nil {
|
||||
log.Errorf("GetLLMContainerInstantModelDriver fail %s", err)
|
||||
return nil
|
||||
}
|
||||
return drv.GetImageInternalPathMounts(model)
|
||||
}
|
||||
|
||||
@@ -800,7 +810,11 @@ func (model *SInstantModel) DoImport(ctx context.Context, userCred mcclient.Toke
|
||||
os.RemoveAll(tmpDir)
|
||||
}()
|
||||
|
||||
drv := GetInstantModelManager().GetLLMContainerDriver(input.LlmType)
|
||||
drv, err := GetInstantModelManager().GetLLMContainerInstantModelDriver(input.LlmType)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "GetLLMContainerInstantModelDriver")
|
||||
return
|
||||
}
|
||||
|
||||
// download model from registry
|
||||
modelId, mounts, err := drv.DownloadModel(ctx, userCred, nil, tmpDir, input.ModelName, input.ModelTag)
|
||||
|
||||
+14
-1
@@ -63,6 +63,14 @@ type SLLM struct {
|
||||
InstantModelQuotaGb int `list:"user" update:"user" create:"optional" default:"0" nullable:"false"`
|
||||
}
|
||||
|
||||
// CustomizeCreate saves Dify customized envs from create input when present.
|
||||
func (llm *SLLM) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
if err := llm.SLLMBase.CustomizeCreate(ctx, userCred, ownerId, query, data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (man *SLLMManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.LLMCreateInput) (*api.LLMCreateInput, error) {
|
||||
var err error
|
||||
input.LLMBaseCreateInput, err = man.SLLMBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.LLMBaseCreateInput)
|
||||
@@ -75,7 +83,7 @@ func (man *SLLMManager) ValidateCreateData(ctx context.Context, userCred mcclien
|
||||
}
|
||||
lSku := sku.(*SLLMSku)
|
||||
input.LLMSkuId = lSku.Id
|
||||
input.LLMImageId = lSku.LLMImageId
|
||||
input.LLMImageId = lSku.GetLLMImageId()
|
||||
|
||||
return input, nil
|
||||
}
|
||||
@@ -116,6 +124,11 @@ func (man *SLLMManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery,
|
||||
}
|
||||
q = q.Equals("llm_image_id", imgObj.GetId())
|
||||
}
|
||||
if len(input.LLMType) > 0 {
|
||||
skuQ := GetLLMSkuManager().Query().SubQuery()
|
||||
q = q.Join(skuQ, sqlchemy.Equals(q.Field("llm_sku_id"), skuQ.Field("id")))
|
||||
q = q.Filter(sqlchemy.Equals(skuQ.Field("llm_type"), input.LLMType))
|
||||
}
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
commonapi "yunion.io/x/onecloud/pkg/apis"
|
||||
@@ -57,7 +58,9 @@ func getDriverWithError[K ~string, D any](drvs *drivers, typ K) (D, error) {
|
||||
return drv.(D), nil
|
||||
}
|
||||
|
||||
type ILLMContainerInstantApp interface {
|
||||
type ILLMContainerInstantModel interface {
|
||||
GetMountedModels(sku *SLLMSku) []string
|
||||
|
||||
GetProbedInstantModelsExt(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, mdlIds ...string) (map[string]llm.LLMInternalInstantMdlInfo, error)
|
||||
DetectModelPaths(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, pkgInfo llm.LLMInternalInstantMdlInfo) ([]string, error)
|
||||
|
||||
@@ -75,14 +78,36 @@ type ILLMContainerInstantApp interface {
|
||||
DownloadModel(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM, tmpDir string, modelName string, modelTag string) (string, []string, error)
|
||||
}
|
||||
|
||||
// ILLMContainerDriverMultiContainer is an optional interface for drivers that create a pod with multiple containers (e.g. Dify). If not implemented, the driver is assumed to provide a single container via GetContainerSpec.
|
||||
type ILLMContainerDriverMultiContainer interface {
|
||||
GetContainerSpecs(ctx context.Context, llm *SLLM, image *SLLMImage, sku *SLLMSku, props []string, devices []computeapi.SIsolatedDevice, diskId string) []*computeapi.PodContainerCreateInput
|
||||
}
|
||||
|
||||
type ILLMContainerDriver interface {
|
||||
GetType() llm.LLMContainerType
|
||||
GetContainerSpec(ctx context.Context, llm *SLLM, image *SLLMImage, sku *SLLMSku, props []string, devices []computeapi.SIsolatedDevice, diskId string) *computeapi.PodContainerCreateInput
|
||||
|
||||
ILLMContainerInstantApp
|
||||
// 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.
|
||||
StartLLM(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM) error
|
||||
|
||||
// GetSpec returns the type-specific spec from the SKU (e.g. *LLMSpecOllama, *LLMSpecDify). Returns nil if not applicable or missing.
|
||||
GetSpec(sku *SLLMSku) interface{}
|
||||
// GetPrimaryImageId returns the primary image id for this SKU type (e.g. LLMImageId for ollama/vllm, DifyApiImageId for dify).
|
||||
GetPrimaryImageId(sku *SLLMSku) string
|
||||
// ValidateCreateSpec validates create input and returns the LLMSpec to store. Called by SKU manager after base validation.
|
||||
ValidateCreateSpec(ctx context.Context, userCred mcclient.TokenCredential, input *llm.LLMSkuCreateInput) (*llm.LLMSpec, error)
|
||||
// ValidateUpdateSpec validates update input, merges with current spec, and returns the LLMSpec to store. Called by SKU when LLMSpec is not nil.
|
||||
ValidateUpdateSpec(ctx context.Context, userCred mcclient.TokenCredential, sku *SLLMSku, input *llm.LLMSkuUpdateInput) (*llm.LLMSpec, error)
|
||||
|
||||
ILLMContainerMCPAgent
|
||||
}
|
||||
|
||||
// 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.
|
||||
type ILLMContainerInstantModelDriver interface {
|
||||
ILLMContainerDriver
|
||||
ILLMContainerInstantModel
|
||||
}
|
||||
|
||||
type ILLMContainerMCPAgent interface {
|
||||
GetLLMUrl(ctx context.Context, userCred mcclient.TokenCredential, llm *SLLM) (string, error)
|
||||
}
|
||||
@@ -102,3 +127,26 @@ func GetLLMContainerDriver(typ llm.LLMContainerType) ILLMContainerDriver {
|
||||
func GetLLMContainerDriverWithError(typ llm.LLMContainerType) (ILLMContainerDriver, error) {
|
||||
return getDriverWithError[llm.LLMContainerType, ILLMContainerDriver](llmContainerDrivers, typ)
|
||||
}
|
||||
|
||||
func GetLLMContainerInstantModelDriver(typ llm.LLMContainerType) (ILLMContainerInstantModelDriver, error) {
|
||||
drv, err := GetLLMContainerDriverWithError(typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if instantDrv, ok := drv.(ILLMContainerInstantModelDriver); ok {
|
||||
return instantDrv, nil
|
||||
}
|
||||
return nil, fmt.Errorf("driver %s does not support instant model operations", typ)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func GetDriverPodContainers(ctx context.Context, drv ILLMContainerDriver, llm *SLLM, image *SLLMImage, sku *SLLMSku, props []string, devices []computeapi.SIsolatedDevice, diskId string) []*computeapi.PodContainerCreateInput {
|
||||
if multi, ok := drv.(ILLMContainerDriverMultiContainer); ok {
|
||||
return multi.GetContainerSpecs(ctx, llm, image, sku, props, devices, diskId)
|
||||
}
|
||||
spec := drv.GetContainerSpec(ctx, llm, image, sku, props, devices, diskId)
|
||||
if spec == nil {
|
||||
return nil
|
||||
}
|
||||
return []*computeapi.PodContainerCreateInput{spec}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,10 @@ func (llm *SLLM) getMountedInstantModels(ctx context.Context, probedExt map[stri
|
||||
}
|
||||
mdlMap := make(map[string]struct{})
|
||||
postOverlays := container.Spec.VolumeMounts[0].Disk.PostOverlay
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
for i := range postOverlays {
|
||||
postOverlay := postOverlays[i]
|
||||
mdlId := drv.GetInstantModelIdByPostOverlay(postOverlay, mdlNameToId)
|
||||
@@ -60,7 +63,10 @@ func (llm *SLLM) getMountedInstantModels(ctx context.Context, probedExt map[stri
|
||||
}
|
||||
|
||||
func (llm *SLLM) getProbedInstantModelsExt(ctx context.Context, userCred mcclient.TokenCredential, instantModelIds ...string) (map[string]apis.LLMInternalInstantMdlInfo, error) {
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return drv.GetProbedInstantModelsExt(ctx, userCred, llm, instantModelIds...)
|
||||
}
|
||||
|
||||
@@ -389,7 +395,10 @@ func (llm *SLLM) RequestUnmountModel(ctx context.Context, userCred mcclient.Toke
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "FetchModels")
|
||||
}
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
if input.LLMStatus == apis.LLM_STATUS_RUNNING {
|
||||
uninstallModels := findModelsToUninstall(allModels, input)
|
||||
@@ -453,14 +462,20 @@ func (llm *SLLM) RequestMountModels(ctx context.Context, userCred mcclient.Token
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "getMountingModelsPostOverlay")
|
||||
}
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
// return nil, nil, nil, nil
|
||||
log.Warningf("driver %s does not support instant model operations", llm.GetLLMContainerDriver().GetType())
|
||||
}
|
||||
var mdlIds []string
|
||||
for i := range models {
|
||||
model := models[i]
|
||||
if input.LLMStatus == apis.LLM_STATUS_RUNNING {
|
||||
err := drv.PreInstallModel(ctx, userCred, llm, &model)
|
||||
if err != nil {
|
||||
log.Errorf("preinstallPackage fail %s", err)
|
||||
if drv != nil {
|
||||
err := drv.PreInstallModel(ctx, userCred, llm, &model)
|
||||
if err != nil {
|
||||
log.Errorf("preinstallPackage fail %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
mdlIds = append(mdlIds, model.InstantModelId)
|
||||
@@ -644,8 +659,9 @@ func (llm *SLLM) UpdateMountedModelFullNames(ctx context.Context, userCred mccli
|
||||
return errors.Wrap(err, "getDeletedModelIds")
|
||||
}
|
||||
}
|
||||
for i := range sku.MountedModels {
|
||||
instMdl, err := GetInstantModelManager().FetchByIdOrName(ctx, userCred, sku.MountedModels[i])
|
||||
mountedModels := sku.GetMountedModels()
|
||||
for i := range mountedModels {
|
||||
instMdl, err := GetInstantModelManager().FetchByIdOrName(ctx, userCred, mountedModels[i])
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "FetchByIdOrName")
|
||||
}
|
||||
@@ -719,7 +735,10 @@ func (llm *SLLM) GetMountedModelsPostOverlay() ([]*commonapi.ContainerVolumeMoun
|
||||
if len(mdls) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
overlays, err := models2overlays(drv, mdls, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "models2overlays")
|
||||
@@ -749,7 +768,10 @@ func (llm *SLLM) getMountingModelsPostOverlay(ctx context.Context, input apis.LL
|
||||
if len(models) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
overlays, err := models2overlays(drv, models, true)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "models2overlays")
|
||||
@@ -757,7 +779,7 @@ func (llm *SLLM) getMountingModelsPostOverlay(ctx context.Context, input apis.LL
|
||||
return models, overlays, nil
|
||||
}
|
||||
|
||||
func models2overlays(drv ILLMContainerDriver, models []SLLMInstantModel, isInstall bool) ([]*commonapi.ContainerVolumeMountDiskPostOverlay, error) {
|
||||
func models2overlays(drv ILLMContainerInstantModelDriver, models []SLLMInstantModel, isInstall bool) ([]*commonapi.ContainerVolumeMountDiskPostOverlay, error) {
|
||||
var errs []error
|
||||
var allDirs []apis.LLMMountDirInfo
|
||||
for i := range models {
|
||||
@@ -786,7 +808,10 @@ func models2overlays(drv ILLMContainerDriver, models []SLLMInstantModel, isInsta
|
||||
}
|
||||
|
||||
func (llm *SLLM) InstallInstantModels(ctx context.Context, userCred mcclient.TokenCredential, dirs []string, mdlIds []string) error {
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return drv.InstallModel(ctx, userCred, llm, dirs, mdlIds)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,11 @@ func GetLLMPodCreateInput(
|
||||
}
|
||||
|
||||
lcd := llm.GetLLMContainerDriver()
|
||||
llmContainer := lcd.GetContainerSpec(ctx, llm, llmImage, sku, nil, nil, "")
|
||||
containers := GetDriverPodContainers(ctx, lcd, llm, llmImage, sku, nil, nil, "")
|
||||
|
||||
data.Pod = &computeapi.PodCreateInput{
|
||||
HostIPC: true,
|
||||
Containers: []*computeapi.PodContainerCreateInput{
|
||||
llmContainer,
|
||||
},
|
||||
HostIPC: true,
|
||||
Containers: containers,
|
||||
}
|
||||
|
||||
return data, nil
|
||||
|
||||
@@ -142,7 +142,10 @@ func (llm *SLLM) DoSaveModelImage(ctx context.Context, userCred mcclient.TokenCr
|
||||
}
|
||||
instantModel := instantModelObj.(*SInstantModel)
|
||||
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetLLMContainerInstantModelDriver")
|
||||
}
|
||||
prefix, saveDirs, err := drv.GetSaveDirectories(instantModel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetSaveDirectories")
|
||||
@@ -197,7 +200,11 @@ func (llm *SLLM) StartSaveModelImageTask(ctx context.Context, userCred mcclient.
|
||||
}
|
||||
|
||||
func (llm *SLLM) detectModelPaths(ctx context.Context, userCred mcclient.TokenCredential, pkgInfo api.LLMInternalInstantMdlInfo) ([]string, error) {
|
||||
return llm.GetLLMContainerDriver().DetectModelPaths(ctx, userCred, llm, pkgInfo)
|
||||
drv, err := GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetLLMContainerInstantModelDriver")
|
||||
}
|
||||
return drv.DetectModelPaths(ctx, userCred, llm, pkgInfo)
|
||||
}
|
||||
|
||||
// HttpGet performs a GET request and returns the response body
|
||||
|
||||
+51
-62
@@ -12,7 +12,6 @@ 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/validators"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
@@ -50,10 +49,10 @@ type SLLMSkuManager struct {
|
||||
|
||||
type SLLMSku struct {
|
||||
SLLMSkuBase
|
||||
SMountedModelsResource
|
||||
// SMountedModelsResource
|
||||
|
||||
LLMImageId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" update:"user"`
|
||||
LLMType string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required"`
|
||||
LLMType string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required"`
|
||||
LLMSpec *api.LLMSpec `json:"llm_spec" length:"long" list:"user" create:"required" update:"user"`
|
||||
}
|
||||
|
||||
func (man *SLLMSkuManager) ListItemFilter(
|
||||
@@ -94,7 +93,9 @@ func (manager *SLLMSkuManager) FetchCustomizeColumns(
|
||||
virows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
for _, sku := range skus {
|
||||
skuIds = append(skuIds, sku.Id)
|
||||
imageIds = append(imageIds, sku.LLMImageId)
|
||||
if imgId := sku.GetLLMImageId(); imgId != "" {
|
||||
imageIds = append(imageIds, imgId)
|
||||
}
|
||||
if sku.Volumes != nil && len(*sku.Volumes) > 0 && len((*sku.Volumes)[0].TemplateId) > 0 {
|
||||
templateIds = append(templateIds, (*sku.Volumes)[0].TemplateId)
|
||||
}
|
||||
@@ -112,14 +113,16 @@ func (manager *SLLMSkuManager) FetchCustomizeColumns(
|
||||
mountedModelIds := make([]string, 0)
|
||||
for i, sku := range skus {
|
||||
res[i].SharableVirtualResourceDetails = virows[i]
|
||||
res[i].LLMType = sku.LLMType
|
||||
res[i].LLMSpec = sku.LLMSpec
|
||||
for _, v := range details {
|
||||
if v.LLMSkuId == sku.Id {
|
||||
res[i].LLMCapacity = v.LLMCapacity
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(sku.MountedModels) > 0 {
|
||||
mountedModelIds = append(mountedModelIds, sku.MountedModels...)
|
||||
if modelIds := sku.GetMountedModels(); len(modelIds) > 0 {
|
||||
mountedModelIds = append(mountedModelIds, modelIds...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +134,10 @@ func (manager *SLLMSkuManager) FetchCustomizeColumns(
|
||||
log.Errorf("FetchModelObjectsByIds InstantModelManager fail %s", err)
|
||||
} else {
|
||||
for i, sku := range skus {
|
||||
if len(sku.MountedModels) > 0 {
|
||||
modelIds := sku.GetMountedModels()
|
||||
if len(modelIds) > 0 {
|
||||
res[i].MountedModelDetails = make([]api.MountedModelInfo, 0)
|
||||
for _, modelId := range sku.MountedModels {
|
||||
for _, modelId := range modelIds {
|
||||
if instModel, ok := instModels[modelId]; ok {
|
||||
info := api.MountedModelInfo{
|
||||
Id: instModel.Id,
|
||||
@@ -152,10 +156,12 @@ func (manager *SLLMSkuManager) FetchCustomizeColumns(
|
||||
err := db.FetchModelObjectsByIds(GetLLMImageManager(), "id", imageIds, &images)
|
||||
if err == nil {
|
||||
for i, sku := range skus {
|
||||
if image, ok := images[sku.LLMImageId]; ok {
|
||||
res[i].Image = image.Name
|
||||
res[i].ImageLabel = image.ImageLabel
|
||||
res[i].ImageName = image.ImageName
|
||||
if imgId := sku.GetLLMImageId(); imgId != "" {
|
||||
if image, ok := images[imgId]; ok {
|
||||
res[i].Image = image.Name
|
||||
res[i].ImageLabel = image.ImageLabel
|
||||
res[i].ImageName = image.ImageName
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -185,38 +191,37 @@ func (man *SLLMSkuManager) ValidateCreateData(ctx context.Context, userCred mccl
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SLLMSkuBaseManager.ValidateCreateData")
|
||||
}
|
||||
if !api.IsLLMContainerType(input.LLMType) {
|
||||
if !api.IsLLMContainerType(input.LLMType) && input.LLMType != string(api.LLM_CONTAINER_DIFY) {
|
||||
return input, errors.Wrap(httperrors.ErrInputParameter, "llm_type must be one of "+strings.Join(api.LLM_CONTAINER_TYPES.List(), ","))
|
||||
}
|
||||
|
||||
imgObj, err := validators.ValidateModel(ctx, userCred, GetLLMImageManager(), &input.LLMImageId)
|
||||
drv, err := GetLLMContainerDriverWithError(api.LLMContainerType(input.LLMType))
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "validate image_id %s", input.LLMImageId)
|
||||
return input, errors.Wrap(err, "get container driver")
|
||||
}
|
||||
llmImage := imgObj.(*SLLMImage)
|
||||
if llmImage.LLMType != input.LLMType {
|
||||
return input, errors.Wrapf(httperrors.ErrInvalidStatus, "image %s is not of type %s", input.LLMImageId, input.LLMType)
|
||||
spec, err := drv.ValidateCreateSpec(ctx, userCred, input)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validate create spec")
|
||||
}
|
||||
input.LLMImageId = llmImage.Id
|
||||
|
||||
if input.MountedModels != nil {
|
||||
for i, mdl := range input.MountedModels {
|
||||
instMdl, err := GetInstantModelManager().FetchByIdOrName(ctx, userCred, mdl)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "validate mounted model %s", mdl)
|
||||
}
|
||||
instantModle := instMdl.(*SInstantModel)
|
||||
if instantModle.LlmType != input.LLMType {
|
||||
return input, errors.Wrapf(httperrors.ErrInvalidStatus, "mounted model %s is not of type %s", mdl, input.LLMType)
|
||||
}
|
||||
input.MountedModels[i] = instantModle.GetId()
|
||||
}
|
||||
}
|
||||
|
||||
input.LLMSpec = spec
|
||||
input.Status = api.STATUS_READY
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// GetLLMImageId returns the primary image id for this SKU. Delegates to driver.
|
||||
func (sku *SLLMSku) GetLLMImageId() string {
|
||||
return sku.GetLLMContainerDriver().GetPrimaryImageId(sku)
|
||||
}
|
||||
|
||||
// GetMountedModels returns mounted model ids (from Ollama or Vllm spec). Delegates to instant-model driver; returns nil for drivers that do not support instant models (e.g. Dify).
|
||||
func (sku *SLLMSku) GetMountedModels() []string {
|
||||
drv, err := GetLLMContainerInstantModelDriver(api.LLMContainerType(sku.LLMType))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return drv.GetMountedModels(sku)
|
||||
}
|
||||
|
||||
func (sku *SLLMSku) GetLLMContainerDriver() ILLMContainerDriver {
|
||||
return GetLLMContainerDriver(api.LLMContainerType(sku.LLMType))
|
||||
}
|
||||
@@ -228,43 +233,27 @@ func (sku *SLLMSku) ValidateUpdateData(ctx context.Context, userCred mcclient.To
|
||||
return input, errors.Wrap(err, "validate LLMSkuBaseUpdateInput")
|
||||
}
|
||||
|
||||
if input.MountedModels != nil {
|
||||
for i, mdl := range input.MountedModels {
|
||||
instMdl, err := GetInstantModelManager().FetchByIdOrName(ctx, userCred, mdl)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "validate mounted model %s", mdl)
|
||||
}
|
||||
instantModle := instMdl.(*SInstantModel)
|
||||
if instantModle.LlmType != sku.LLMType {
|
||||
return input, errors.Wrapf(httperrors.ErrInvalidStatus, "mounted model %s is not of type %s", mdl, sku.LLMType)
|
||||
}
|
||||
input.MountedModels[i] = instantModle.GetId()
|
||||
}
|
||||
if sku.LLMSpec == nil {
|
||||
return input, nil
|
||||
}
|
||||
|
||||
if input.LLMImageId != "" {
|
||||
imgObj, err := validators.ValidateModel(ctx, userCred, GetLLMImageManager(), &input.LLMImageId)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "validate image_id %s", input.LLMImageId)
|
||||
}
|
||||
llmImage := imgObj.(*SLLMImage)
|
||||
if llmImage.LLMType != sku.LLMType {
|
||||
return input, errors.Wrapf(httperrors.ErrInvalidStatus, "image %s is not of type %s", input.LLMImageId, sku.LLMType)
|
||||
}
|
||||
input.LLMImageId = llmImage.Id
|
||||
log.Infof("update llm_image_id %s to %s", sku.LLMImageId, input.LLMImageId)
|
||||
drv := sku.GetLLMContainerDriver()
|
||||
spec, err := drv.ValidateUpdateSpec(ctx, userCred, sku, &input)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validate update spec")
|
||||
}
|
||||
if spec != nil {
|
||||
input.LLMSpec = spec
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (sku *SLLMSku) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
|
||||
count, err := GetLLMManager().Query().Equals("llm_sku_id", sku.Id).CountWithError()
|
||||
if nil != err {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetch llm")
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.Wrap(errors.ErrNotSupported, "This sku is currently in use")
|
||||
return errors.Wrap(errors.ErrNotSupported, "This sku is currently in use by LLM")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -150,12 +150,12 @@ func InitHandlers(app *appsrv.Application, isSlave bool) {
|
||||
|
||||
models.GetLLMImageManager(),
|
||||
models.GetLLMSkuManager(),
|
||||
models.GetDifySkuManager(),
|
||||
// models.GetDifySkuManager(),
|
||||
models.GetVolumeManager(),
|
||||
models.GetAccessInfoManager(),
|
||||
models.GetLLMContainerManager(),
|
||||
models.GetLLMManager(),
|
||||
models.GetDifyManager(),
|
||||
// models.GetDifyManager(),
|
||||
models.GetInstantModelManager(),
|
||||
models.GetLLMInstantModelManager(),
|
||||
models.GetMCPAgentManager(),
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package dify
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
type DifyBatchCreateTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(DifyBatchCreateTask{})
|
||||
}
|
||||
|
||||
func (task *DifyBatchCreateTask) OnInit(ctx context.Context, objs []db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
task.SetStage("OnDifyCreateCompleteAll", nil)
|
||||
|
||||
inputs := make([]api.DifyCreateInput, 0)
|
||||
task.GetParams().Unmarshal(&inputs, "data")
|
||||
|
||||
for i := range objs {
|
||||
dify := objs[i].(*models.SDify)
|
||||
err := dify.StartCreateTask(ctx, task.UserCred, inputs[i], task.GetTaskId())
|
||||
if err != nil {
|
||||
log.Errorf("start task for %d dify %s(%s) fail %s", i, dify.Id, dify.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (task *DifyBatchCreateTask) OnDifyCreateCompleteAll(ctx context.Context, objs []db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
task.SetStageComplete(ctx, nil)
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
package dify
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
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/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type DifyCreateTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(DifyCreateTask{})
|
||||
}
|
||||
|
||||
func (task *DifyCreateTask) taskFailed(ctx context.Context, dify *models.SDify, err error) {
|
||||
dify.SetStatus(ctx, task.UserCred, api.LLM_STATUS_CREATE_FAIL, err.Error())
|
||||
db.OpsLog.LogEvent(dify, db.ACT_CREATE, err, task.UserCred)
|
||||
logclient.AddActionLogWithStartable(task, dify, logclient.ACT_CREATE, err, task.UserCred, false)
|
||||
task.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
func (task *DifyCreateTask) taskComplete(ctx context.Context, dify *models.SDify, status string) {
|
||||
dify.SetStatus(ctx, task.GetUserCred(), status, "create success")
|
||||
task.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (task *DifyCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
dify := obj.(*models.SDify)
|
||||
serverCreateInput := api.DifyCreateInput{}
|
||||
err := body.Unmarshal(&serverCreateInput)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, dify, err)
|
||||
return
|
||||
}
|
||||
|
||||
serverCreateInput.Name = dify.Name
|
||||
|
||||
task.SetStage("OnDifyRefreshStatusComplete", nil)
|
||||
s := auth.GetSession(ctx, task.GetUserCred(), "")
|
||||
s.WithTaskCallback(task.GetId(), func() error {
|
||||
serverId, err := dify.ServerCreate(ctx, task.UserCred, s, &serverCreateInput)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, dify, err)
|
||||
return err
|
||||
}
|
||||
|
||||
db.Update(dify, func() error {
|
||||
dify.CmpId = serverId
|
||||
return nil
|
||||
})
|
||||
dify.CmpId = serverId
|
||||
return nil
|
||||
})
|
||||
// var expectStatus []string
|
||||
// if serverCreateInput.AutoStart {
|
||||
// expectStatus = []string{computeapi.VM_RUNNING}
|
||||
// } else {
|
||||
// expectStatus = []string{computeapi.VM_READY}
|
||||
// }
|
||||
// taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
// server, err := dify.WaitServerStatus(ctx, task.UserCred, expectStatus, 7200)
|
||||
// if err != nil {
|
||||
// return nil, errors.Wrap(err, "WaitServerStatus")
|
||||
// }
|
||||
// return jsonutils.Marshal(server), nil
|
||||
// })
|
||||
}
|
||||
|
||||
func (task *DifyCreateTask) OnDifyRefreshStatusCompleteFailed(ctx context.Context, dify *models.SDify, err jsonutils.JSONObject) {
|
||||
task.taskFailed(ctx, dify, errors.Error(err.String()))
|
||||
}
|
||||
|
||||
func (task *DifyCreateTask) OnDifyRefreshStatusComplete(ctx context.Context, dify *models.SDify, body jsonutils.JSONObject) {
|
||||
server, err := dify.GetServer(ctx)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, dify, errors.Wrap(err, "GetServer"))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建磁盘
|
||||
for _, disk := range server.DisksInfo {
|
||||
volume := models.SVolume{}
|
||||
volume.CmpId = disk.Id
|
||||
volume.LLMId = dify.Id
|
||||
volume.SizeMB = disk.SizeMb
|
||||
volume.Name = disk.Name
|
||||
volume.StorageType = disk.StorageType
|
||||
volume.Status = computeapi.DISK_READY
|
||||
volume.DomainId = dify.DomainId
|
||||
volume.ProjectId = dify.ProjectId
|
||||
volume.ProjectSrc = dify.ProjectSrc
|
||||
// if len(input.TemplateId) > 0 {
|
||||
volume.TemplateId = disk.ImageId
|
||||
// }
|
||||
// volume.MountedApps = mountedApps
|
||||
|
||||
err := models.GetVolumeManager().TableSpec().Insert(ctx, &volume)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, dify, errors.Wrap(err, "VolumeManager.TableSpec().Insert"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 创建访问信息、portmappings
|
||||
if len(server.Nics) > 0 {
|
||||
db.Update(dify, func() error {
|
||||
dify.LLMIp = server.Nics[0].IpAddr
|
||||
return nil
|
||||
})
|
||||
|
||||
for _, portMapping := range server.Nics[0].PortMappings {
|
||||
access := models.SAccessInfo{}
|
||||
access.LLMId = dify.Id
|
||||
|
||||
access.ListenPort = int(portMapping.Port)
|
||||
access.AccessPort = int(*portMapping.HostPort)
|
||||
access.Protocol = string(portMapping.Protocol)
|
||||
access.RemoteIps = portMapping.RemoteIps
|
||||
envs := make([]api.PortMappingEnv, 0)
|
||||
for _, env := range portMapping.Envs {
|
||||
envs = append(envs, api.PortMappingEnv{
|
||||
Key: env.Key,
|
||||
ValueFrom: string(env.ValueFrom),
|
||||
})
|
||||
}
|
||||
access.PortMappingEnvs = envs
|
||||
|
||||
models.GetAccessInfoManager().TableSpec().Insert(ctx, &access)
|
||||
}
|
||||
}
|
||||
|
||||
// // 创建应用容器记录
|
||||
// if len(server.Containers) != 1 {
|
||||
// task.taskFailed(ctx, dify, errors.Errorf("expected 1 containers, but got %d", len(server.Containers)))
|
||||
// return
|
||||
// }
|
||||
// llmCtr := models.GetSvrLLMContainer(server.Containers)
|
||||
// if llmCtr == nil {
|
||||
// task.taskFailed(ctx, dify, errors.Errorf("cannot find app container"))
|
||||
// return
|
||||
// }
|
||||
// if _, err := models.GetLLMContainerManager().CreateOnLLM(ctx, task.GetUserCred(), dify.GetOwnerId(), dify, llmCtr.Id, llmCtr.Name); nil != err {
|
||||
// task.taskFailed(ctx, dify, errors.Wrap(err, "create llm container on llm"))
|
||||
// return
|
||||
// }
|
||||
|
||||
task.taskComplete(ctx, dify, server.Status)
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package dify
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"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/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type DifyDeleteTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(DifyDeleteTask{})
|
||||
}
|
||||
|
||||
func (task *DifyDeleteTask) taskFailed(ctx context.Context, dify *models.SDify, err error) {
|
||||
dify.SetStatus(ctx, task.UserCred, api.LLM_STATUS_DELETE_FAILED, err.Error())
|
||||
db.OpsLog.LogEvent(dify, db.ACT_DELETE_FAIL, err, task.UserCred)
|
||||
logclient.AddActionLogWithStartable(task, dify, logclient.ACT_DELETE, err, task.UserCred, false)
|
||||
task.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
func (task *DifyDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
dify := obj.(*models.SDify)
|
||||
dify.SetStatus(ctx, task.UserCred, api.LLM_STATUS_DELETING, "start delete")
|
||||
|
||||
if len(dify.CmpId) == 0 {
|
||||
task.OnDifyRefreshStatusComplete(ctx, dify, nil)
|
||||
return
|
||||
}
|
||||
|
||||
task.SetStage("OnDifyRefreshStatusComplete", nil)
|
||||
s := auth.GetSession(ctx, task.GetUserCred(), "")
|
||||
err := s.WithTaskCallback(task.GetId(), func() error {
|
||||
return dify.ServerDelete(ctx, task.UserCred, s)
|
||||
})
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, dify, err)
|
||||
return
|
||||
}
|
||||
// taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
// err = dify.WaitDelete(ctx, task.UserCred, 1800)
|
||||
// if err != nil {
|
||||
// return nil, errors.Wrap(err, "llm.WaitDelete")
|
||||
// }
|
||||
|
||||
// return nil, nil
|
||||
// })
|
||||
}
|
||||
|
||||
func (task *DifyDeleteTask) OnDifyRefreshStatusCompleteFailed(ctx context.Context, dify *models.SDify, err jsonutils.JSONObject) {
|
||||
task.taskFailed(ctx, dify, errors.Error(err.String()))
|
||||
}
|
||||
|
||||
func (task *DifyDeleteTask) OnDifyRefreshStatusComplete(ctx context.Context, dify *models.SDify, body jsonutils.JSONObject) {
|
||||
volume, err := dify.GetVolume()
|
||||
if err != nil {
|
||||
if errors.Cause(err) != errors.ErrNotFound {
|
||||
task.taskFailed(ctx, dify, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if volume != nil {
|
||||
task.SetStage("OnDifyVolumeDeleteComplete", nil)
|
||||
volume.StartDeleteTask(ctx, task.UserCred, task.GetTaskId())
|
||||
} else {
|
||||
task.OnDifyVolumeDeleteComplete(ctx, dify, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (task *DifyDeleteTask) OnDifyVolumeDeleteCompleteFailed(ctx context.Context, dify *models.SDify, err jsonutils.JSONObject) {
|
||||
task.taskFailed(ctx, dify, errors.Error(err.String()))
|
||||
}
|
||||
|
||||
func (task *DifyDeleteTask) OnDifyVolumeDeleteComplete(ctx context.Context, dify *models.SDify, body jsonutils.JSONObject) {
|
||||
err := dify.RealDelete(ctx, task.UserCred)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, dify, err)
|
||||
return
|
||||
}
|
||||
task.SetStageComplete(ctx, nil)
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package dify
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
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/llm/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type DifyStartTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(DifyStartTask{})
|
||||
}
|
||||
|
||||
func (task *DifyStartTask) taskFailed(ctx context.Context, dify *models.SDify, err string) {
|
||||
dify.SetStatus(ctx, task.UserCred, api.LLM_STATUS_START_FAIL, err)
|
||||
db.OpsLog.LogEvent(dify, db.ACT_START, err, task.UserCred)
|
||||
logclient.AddActionLogWithStartable(task, dify, logclient.ACT_START, err, task.UserCred, false)
|
||||
// llm.NotifyRequest(ctx, task.GetUserCred(), notify.ActionStart, nil, false)
|
||||
task.SetStageFailed(ctx, jsonutils.NewString(err))
|
||||
}
|
||||
|
||||
func (task *DifyStartTask) taskComplete(ctx context.Context, dify *models.SDify) {
|
||||
dify.SetStatus(ctx, task.GetUserCred(), api.LLM_STATUS_RUNNING, "start complete")
|
||||
// llm.NotifyRequest(ctx, task.GetUserCred(), notify.ActionStart, nil, true)
|
||||
task.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (t *DifyStartTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
t.requestStart(ctx, obj.(*models.SDify))
|
||||
}
|
||||
|
||||
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.CmpId, "start", nil)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.taskFailed(ctx, dify, err.Error())
|
||||
return
|
||||
}
|
||||
// worker.StartTaskRun(t, func() (jsonutils.JSONObject, error) {
|
||||
// _, err := dify.WaitServerStatus(ctx, t.GetUserCred(), []string{computeapi.VM_RUNNING}, 900)
|
||||
// if err != nil {
|
||||
// return nil, errors.Wrap(err, "WaitServerStatus")
|
||||
// }
|
||||
// time.Sleep(time.Second)
|
||||
// _, err = d.WaitServerStatus(ctx, task.UserCred, []string{computeapi.VM_RUNNING}, 900)
|
||||
// if err != nil {
|
||||
// return nil, errors.Wrap(err, "WaitServerStatus")
|
||||
// }
|
||||
// return nil, nil
|
||||
// })
|
||||
// if err := llm.RunModel(ctx, t.GetUserCred()); nil != err {
|
||||
// t.OnStartedFailed(ctx, llm, jsonutils.NewString(err.Error()))
|
||||
// return
|
||||
// }
|
||||
}
|
||||
|
||||
func (t *DifyStartTask) OnStartedFailed(ctx context.Context, dify *models.SDify, err jsonutils.JSONObject) {
|
||||
t.taskFailed(ctx, dify, err.String())
|
||||
}
|
||||
|
||||
func (t *DifyStartTask) OnStarted(ctx context.Context, dify *models.SDify, reason jsonutils.JSONObject) {
|
||||
t.taskComplete(ctx, dify)
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package dify
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
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/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type DifyStopTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(DifyStopTask{})
|
||||
}
|
||||
|
||||
func (task *DifyStopTask) taskFailed(ctx context.Context, dify *models.SDify, err string) {
|
||||
dify.SetStatus(ctx, task.UserCred, api.LLM_STATUS_STOP_FAILED, err)
|
||||
db.OpsLog.LogEvent(dify, db.ACT_STOP, err, task.UserCred)
|
||||
logclient.AddActionLogWithStartable(task, dify, logclient.ACT_VM_STOP, err, task.UserCred, false)
|
||||
// llm.NotifyRequest(ctx, task.GetUserCred(), notify.ActionStop, nil, false)
|
||||
task.SetStageFailed(ctx, jsonutils.NewString(err))
|
||||
}
|
||||
|
||||
func (task *DifyStopTask) taskComplete(ctx context.Context, dify *models.SDify) {
|
||||
if !task.HasParentTask() {
|
||||
dify.SetStatus(ctx, task.GetUserCred(), api.LLM_STATUS_READY, "")
|
||||
}
|
||||
// llm.NotifyRequest(ctx, task.GetUserCred(), notify.ActionStop, nil, true)
|
||||
task.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (task *DifyStopTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
dify := obj.(*models.SDify)
|
||||
|
||||
srv, err := dify.GetServer(ctx)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, dify, errors.Wrap(err, "GetServer").Error())
|
||||
return
|
||||
}
|
||||
|
||||
if srv.Status == computeapi.VM_READY {
|
||||
task.taskComplete(ctx, dify)
|
||||
return
|
||||
}
|
||||
|
||||
task.SetStage("OnStopComplete", nil)
|
||||
s := auth.GetSession(ctx, task.UserCred, "")
|
||||
s.WithTaskCallback(task.GetId(), func() error {
|
||||
_, err = compute.Servers.PerformAction(s, dify.CmpId, "stop", nil)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, dify, err.Error())
|
||||
return
|
||||
}
|
||||
// taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
// s := auth.GetSession(ctx, task.UserCred, "")
|
||||
// _, err = compute.Servers.PerformAction(s, dify.SvrId, "stop", nil)
|
||||
// if err != nil {
|
||||
// task.taskFailed(ctx, dify, err.Error())
|
||||
// return nil, errors.Wrap(err, "server perform stop")
|
||||
// }
|
||||
// _, err := dify.WaitServerStatus(ctx, task.UserCred, []string{computeapi.VM_READY}, 600)
|
||||
// if err != nil {
|
||||
// if errors.Cause(err) == errors.ErrTimeout {
|
||||
// params := computeapi.ServerStopInput{
|
||||
// IsForce: true,
|
||||
// TimeoutSecs: 10,
|
||||
// }
|
||||
// _, err = compute.Servers.PerformAction(s, dify.SvrId, "stop", jsonutils.Marshal(params))
|
||||
// if err != nil {
|
||||
// return nil, errors.Wrap(err, "server perform stop by force")
|
||||
// }
|
||||
// _, err := dify.WaitServerStatus(ctx, task.UserCred, []string{computeapi.VM_READY}, 600)
|
||||
// if err != nil {
|
||||
// return nil, errors.Wrap(err, "WaitServerStatus 2")
|
||||
// }
|
||||
// } else {
|
||||
// return nil, errors.Wrap(err, "WaitServerStatus")
|
||||
// }
|
||||
// }
|
||||
// return nil, nil
|
||||
// })
|
||||
}
|
||||
|
||||
func (task *DifyStopTask) OnStopComplete(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
dify := obj.(*models.SDify)
|
||||
task.taskComplete(ctx, dify)
|
||||
}
|
||||
|
||||
func (task *DifyStopTask) OnStopCompleteFailed(ctx context.Context, obj db.IStandaloneModel, err jsonutils.JSONObject) {
|
||||
dify := obj.(*models.SDify)
|
||||
task.taskFailed(ctx, dify, err.String())
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package dify // import "yunion.io/x/onecloud/pkg/llm/tasks/dify"
|
||||
@@ -148,18 +148,46 @@ func (task *LLMCreateTask) OnLLMRefreshStatusComplete(ctx context.Context, llm *
|
||||
}
|
||||
}
|
||||
|
||||
// 创建应用容器记录
|
||||
if len(server.Containers) != 1 {
|
||||
task.taskFailed(ctx, llm, errors.Errorf("expected 1 containers, but got %d", len(server.Containers)))
|
||||
return
|
||||
// 创建应用容器记录(多容器 driver 如 Dify 不创建 SLLMContainer)
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
_, isMulti := drv.(models.ILLMContainerDriverMultiContainer)
|
||||
if !isMulti {
|
||||
if len(server.Containers) != 1 {
|
||||
task.taskFailed(ctx, llm, errors.Errorf("expected 1 containers, but got %d", len(server.Containers)))
|
||||
return
|
||||
}
|
||||
llmCtr := models.GetSvrLLMContainer(server.Containers)
|
||||
if llmCtr == nil {
|
||||
task.taskFailed(ctx, llm, errors.Errorf("cannot find app container"))
|
||||
return
|
||||
}
|
||||
if _, err := models.GetLLMContainerManager().CreateOnLLM(ctx, task.GetUserCred(), llm.GetOwnerId(), llm, llmCtr.Id, llmCtr.Name); nil != err {
|
||||
task.taskFailed(ctx, llm, errors.Wrap(err, "create llm container on llm"))
|
||||
return
|
||||
}
|
||||
}
|
||||
llmCtr := models.GetSvrLLMContainer(server.Containers)
|
||||
if llmCtr == nil {
|
||||
task.taskFailed(ctx, llm, errors.Errorf("cannot find app container"))
|
||||
return
|
||||
}
|
||||
if _, err := models.GetLLMContainerManager().CreateOnLLM(ctx, task.GetUserCred(), llm.GetOwnerId(), llm, llmCtr.Id, llmCtr.Name); nil != err {
|
||||
task.taskFailed(ctx, llm, errors.Wrap(err, "create llm container on llm"))
|
||||
|
||||
// When AutoStart was true, compute auto-starts the server so LLMStartTask is never run. We must run StartLLM here.
|
||||
var createInput api.LLMCreateInput
|
||||
if task.GetParams() != nil && task.GetParams().Unmarshal(&createInput) == nil && createInput.AutoStart {
|
||||
_, err = llm.WaitServerStatus(ctx, task.GetUserCred(), []string{computeapi.VM_RUNNING}, 7200)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, llm, errors.Wrap(err, "WaitServerStatus VM_RUNNING"))
|
||||
return
|
||||
}
|
||||
if !isMulti {
|
||||
_, err = llm.WaitContainerStatus(ctx, task.GetUserCred(), []string{computeapi.CONTAINER_STATUS_RUNNING}, 120)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, llm, errors.Wrap(err, "WaitContainerStatus"))
|
||||
return
|
||||
}
|
||||
}
|
||||
err = llm.GetLLMContainerDriver().StartLLM(ctx, task.GetUserCred(), llm)
|
||||
if err != nil {
|
||||
task.taskFailed(ctx, llm, errors.Wrap(err, "StartLLM"))
|
||||
return
|
||||
}
|
||||
task.taskComplete(ctx, llm, api.LLM_STATUS_RUNNING)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -191,8 +191,10 @@ func (task *LLMInstantModelsSyncTask) OnModelsMountCompleteFailed(ctx context.Co
|
||||
dupMarker := "duplicated container target dirs map"
|
||||
dupIndex := strings.Index(errStr, dupMarker)
|
||||
if dupIndex != -1 {
|
||||
drv := llm.GetLLMContainerDriver()
|
||||
errStr = drv.CheckDuplicateMounts(errStr, dupIndex)
|
||||
drv, err := models.GetLLMContainerInstantModelDriver(llm.GetLLMContainerDriver().GetType())
|
||||
if err == nil {
|
||||
errStr = drv.CheckDuplicateMounts(errStr, dupIndex)
|
||||
}
|
||||
}
|
||||
|
||||
task.taskFailed(ctx, llm, errStr)
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
_ "yunion.io/x/onecloud/pkg/llm/tasks/dify"
|
||||
_ "yunion.io/x/onecloud/pkg/llm/tasks/llm"
|
||||
_ "yunion.io/x/onecloud/pkg/llm/tasks/llm_container"
|
||||
_ "yunion.io/x/onecloud/pkg/llm/tasks/volume"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
var (
|
||||
Difies DifyManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
Difies = DifyManager{
|
||||
modules.NewLLMManager("dify", "difies",
|
||||
[]string{"ID", "Name", "Guest_ID", "Containers", "Status"},
|
||||
[]string{}),
|
||||
}
|
||||
modules.Register(&Difies)
|
||||
}
|
||||
|
||||
type DifyManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
type DifySkuManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
var (
|
||||
DifySku DifySkuManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
DifySku = DifySkuManager{
|
||||
ResourceManager: modules.NewLLMManager("dify_sku", "dify_skus",
|
||||
[]string{},
|
||||
[]string{},
|
||||
),
|
||||
}
|
||||
modules.Register(&DifySku)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/cmdline"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
@@ -23,6 +24,7 @@ func (o *DifyListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
if o.Used != nil {
|
||||
params.Set("unused", jsonutils.JSONFalse)
|
||||
}
|
||||
params.Set("llm_type", jsonutils.NewString(string(api.LLM_CONTAINER_DIFY)))
|
||||
return params, nil
|
||||
}
|
||||
|
||||
@@ -37,7 +39,7 @@ func (o *DifyShowOptions) Params() (jsonutils.JSONObject, error) {
|
||||
type DifyCreateOptions struct {
|
||||
LLMBaseCreateOptions
|
||||
|
||||
DIFY_SKU_ID string `help:"dify sku id or name" json:"dify_sku_id"`
|
||||
DIFY_SKU_ID string `help:"dify sku id or name" json:"llm_sku_id"`
|
||||
}
|
||||
|
||||
func (o *DifyCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package llm
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
@@ -11,7 +12,12 @@ type DifySkuListOptions struct {
|
||||
}
|
||||
|
||||
func (o *DifySkuListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(o)
|
||||
dict, err := options.ListStructToParams(o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dict.Set("llm_type", jsonutils.NewString(string(api.LLM_CONTAINER_DIFY)))
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
type DifySkuShowOptions struct {
|
||||
@@ -25,15 +31,16 @@ func (o *DifySkuShowOptions) Params() (jsonutils.JSONObject, error) {
|
||||
type DifySkuCreateOptions struct {
|
||||
LLMSkuBaseCreateOptions
|
||||
|
||||
POSTGRES_IMAGE_ID string `json:"postgres_image_id"`
|
||||
REDIS_IMAGE_ID string `json:"redis_image_id"`
|
||||
NGINX_IMAGE_ID string `json:"nginx_image_id"`
|
||||
DIFY_API_IMAGE_ID string `json:"dify_api_image_id"`
|
||||
DIFY_PLUGIN_IMAGE_ID string `json:"dify_plugin_image_id"`
|
||||
DIFY_WEB_IMAGE_ID string `json:"dify_web_image_id"`
|
||||
DIFY_SANDBOX_IMAGE_ID string `json:"dify_sandbox_image_id"`
|
||||
DIFY_SSRF_IMAGE_ID string `json:"dify_ssrf_image_id"`
|
||||
DIFY_WEAVIATE_IMAGE_ID string `json:"dify_weaviate_image_id"`
|
||||
POSTGRES_IMAGE_ID string `json:"postgres_image_id"`
|
||||
REDIS_IMAGE_ID string `json:"redis_image_id"`
|
||||
NGINX_IMAGE_ID string `json:"nginx_image_id"`
|
||||
DIFY_API_IMAGE_ID string `json:"dify_api_image_id"`
|
||||
DIFY_PLUGIN_IMAGE_ID string `json:"dify_plugin_image_id"`
|
||||
DIFY_WEB_IMAGE_ID string `json:"dify_web_image_id"`
|
||||
DIFY_SANDBOX_IMAGE_ID string `json:"dify_sandbox_image_id"`
|
||||
DIFY_SSRF_IMAGE_ID string `json:"dify_ssrf_image_id"`
|
||||
DIFY_WEAVIATE_IMAGE_ID string `json:"dify_weaviate_image_id"`
|
||||
CustomizedEnvs []*api.DifyCustomizedEnv `json:"customized_envs,omitempty"`
|
||||
}
|
||||
|
||||
func (o *DifySkuCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
@@ -41,7 +48,31 @@ func (o *DifySkuCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
obj := jsonutils.Marshal(o)
|
||||
obj.Unmarshal(dict)
|
||||
|
||||
o.LLMSkuBaseCreateOptions.Params(dict)
|
||||
// Remove image id keys from top level; we put them in llm_spec
|
||||
for _, k := range []string{"postgres_image_id", "redis_image_id", "nginx_image_id", "dify_api_image_id", "dify_plugin_image_id", "dify_web_image_id", "dify_sandbox_image_id", "dify_ssrf_image_id", "dify_weaviate_image_id"} {
|
||||
dict.Remove(k)
|
||||
}
|
||||
if err := o.LLMSkuBaseCreateOptions.Params(dict); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dict.Set("llm_type", jsonutils.NewString(string(api.LLM_CONTAINER_DIFY)))
|
||||
spec := &api.LLMSpec{
|
||||
Ollama: nil,
|
||||
Vllm: nil,
|
||||
Dify: &api.LLMSpecDify{
|
||||
PostgresImageId: o.POSTGRES_IMAGE_ID,
|
||||
RedisImageId: o.REDIS_IMAGE_ID,
|
||||
NginxImageId: o.NGINX_IMAGE_ID,
|
||||
DifyApiImageId: o.DIFY_API_IMAGE_ID,
|
||||
DifyPluginImageId: o.DIFY_PLUGIN_IMAGE_ID,
|
||||
DifyWebImageId: o.DIFY_WEB_IMAGE_ID,
|
||||
DifySandboxImageId: o.DIFY_SANDBOX_IMAGE_ID,
|
||||
DifySSRFImageId: o.DIFY_SSRF_IMAGE_ID,
|
||||
DifyWeaviateImageId: o.DIFY_WEAVIATE_IMAGE_ID,
|
||||
CustomizedEnvs: o.CustomizedEnvs,
|
||||
},
|
||||
}
|
||||
dict.Set("llm_spec", jsonutils.Marshal(spec))
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
@@ -60,15 +91,16 @@ func (o *DifySkuDeleteOptions) Params() (jsonutils.JSONObject, error) {
|
||||
type DifySkuUpdateOptions struct {
|
||||
LLMSkuBaseUpdateOptions
|
||||
|
||||
PostgresImageID string `json:"postgres_image_id"`
|
||||
RedisImageID string `json:"redis_image_id"`
|
||||
NginxImageID string `json:"nginx_image_id"`
|
||||
DifyApiImageID string `json:"dify_api_image_id"`
|
||||
DifyPluginImageID string `json:"dify_plugin_image_id"`
|
||||
DifyWebImageID string `json:"dify_web_image_id"`
|
||||
DifySandboxImageID string `json:"dify_sandbox_image_id"`
|
||||
DifySsrfImageID string `json:"dify_ssrf_image_id"`
|
||||
DifyWeaviateImageID string `json:"dify_weaviate_image_id"`
|
||||
PostgresImageId string `json:"postgres_image_id"`
|
||||
RedisImageId string `json:"redis_image_id"`
|
||||
NginxImageId string `json:"nginx_image_id"`
|
||||
DifyApiImageId string `json:"dify_api_image_id"`
|
||||
DifyPluginImageId string `json:"dify_plugin_image_id"`
|
||||
DifyWebImageId string `json:"dify_web_image_id"`
|
||||
DifySandboxImageId string `json:"dify_sandbox_image_id"`
|
||||
DifySSRFImageId string `json:"dify_ssrf_image_id"`
|
||||
DifyWeaviateImageId string `json:"dify_weaviate_image_id"`
|
||||
CustomizedEnvs []*api.DifyCustomizedEnv `json:"customized_envs,omitempty"`
|
||||
}
|
||||
|
||||
func (o *DifySkuUpdateOptions) GetId() string {
|
||||
@@ -80,6 +112,34 @@ func (o *DifySkuUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
obj := jsonutils.Marshal(o)
|
||||
obj.Unmarshal(dict)
|
||||
|
||||
o.LLMSkuBaseUpdateOptions.Params(dict)
|
||||
// Remove image id keys from top level; put them in llm_spec when any is set
|
||||
for _, k := range []string{"postgres_image_id", "redis_image_id", "nginx_image_id", "dify_api_image_id", "dify_plugin_image_id", "dify_web_image_id", "dify_sandbox_image_id", "dify_ssrf_image_id", "dify_weaviate_image_id"} {
|
||||
dict.Remove(k)
|
||||
}
|
||||
if err := o.LLMSkuBaseUpdateOptions.Params(dict); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hasImageId := o.PostgresImageId != "" || o.RedisImageId != "" || o.NginxImageId != "" ||
|
||||
o.DifyApiImageId != "" || o.DifyPluginImageId != "" || o.DifyWebImageId != "" ||
|
||||
o.DifySandboxImageId != "" || o.DifySSRFImageId != "" || o.DifyWeaviateImageId != ""
|
||||
if hasImageId || len(o.CustomizedEnvs) > 0 {
|
||||
spec := &api.LLMSpec{
|
||||
Ollama: nil,
|
||||
Vllm: nil,
|
||||
Dify: &api.LLMSpecDify{
|
||||
PostgresImageId: o.PostgresImageId,
|
||||
RedisImageId: o.RedisImageId,
|
||||
NginxImageId: o.NginxImageId,
|
||||
DifyApiImageId: o.DifyApiImageId,
|
||||
DifyPluginImageId: o.DifyPluginImageId,
|
||||
DifyWebImageId: o.DifyWebImageId,
|
||||
DifySandboxImageId: o.DifySandboxImageId,
|
||||
DifySSRFImageId: o.DifySSRFImageId,
|
||||
DifyWeaviateImageId: o.DifyWeaviateImageId,
|
||||
CustomizedEnvs: o.CustomizedEnvs,
|
||||
},
|
||||
}
|
||||
dict.Set("llm_spec", jsonutils.Marshal(spec))
|
||||
}
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@ package llm
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type LLMSkuListOptions struct {
|
||||
options.BaseListOptions
|
||||
|
||||
LLMType string `json:"llm_type" choices:"ollama"`
|
||||
LLMType string `json:"llm_type" choices:"ollama|vllm|dify"`
|
||||
}
|
||||
|
||||
func (o *LLMSkuListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
@@ -30,16 +31,29 @@ type LLMSkuCreateOptions struct {
|
||||
MountedModels []string `help:"mounted models, <model_id> e.g. qwen2:0.5b-dup" json:"mounted_models"`
|
||||
|
||||
LLM_IMAGE_ID string `json:"llm_image_id"`
|
||||
LLM_TYPE string `json:"llm_type" choices:"ollama"`
|
||||
LLM_TYPE string `json:"llm_type" choices:"ollama|vllm"`
|
||||
|
||||
PreferredModel string `help:"preferred model (vllm only), sets llm_spec.vllm.preferred_model" json:"-"`
|
||||
}
|
||||
|
||||
func (o *LLMSkuCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
dict := jsonutils.NewDict()
|
||||
obj := jsonutils.Marshal(o)
|
||||
obj.Unmarshal(dict)
|
||||
|
||||
o.LLMSkuBaseCreateOptions.Params(dict)
|
||||
if err := o.LLMSkuBaseCreateOptions.Params(dict); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fetchMountedModels(o.MountedModels, dict)
|
||||
if o.LLM_TYPE == string(api.LLM_CONTAINER_VLLM) && len(o.PreferredModel) > 0 {
|
||||
spec := &api.LLMSpec{
|
||||
Ollama: nil,
|
||||
Vllm: &api.LLMSpecVllm{
|
||||
PreferredModel: o.PreferredModel,
|
||||
},
|
||||
Dify: nil,
|
||||
}
|
||||
dict.Set("llm_spec", jsonutils.Marshal(spec))
|
||||
}
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
@@ -60,7 +74,10 @@ type LLMSkuUpdateOptions struct {
|
||||
|
||||
MountedModels []string `help:"mounted models, <model_id> e.g. qwen2:0.5b-dup" json:"mounted_models"`
|
||||
|
||||
// For ollama/vllm; backend merges into LLMSpec. Use dify-sku update for dify type.
|
||||
LlmImageId string `json:"llm_image_id"`
|
||||
|
||||
PreferredModel string `help:"preferred model (vllm only), sets llm_spec.vllm.preferred_model" json:"-"`
|
||||
}
|
||||
|
||||
func (o *LLMSkuUpdateOptions) GetId() string {
|
||||
@@ -71,8 +88,19 @@ func (o *LLMSkuUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
dict := jsonutils.NewDict()
|
||||
obj := jsonutils.Marshal(o)
|
||||
obj.Unmarshal(dict)
|
||||
|
||||
o.LLMSkuBaseUpdateOptions.Params(dict)
|
||||
if err := o.LLMSkuBaseUpdateOptions.Params(dict); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fetchMountedModels(o.MountedModels, dict)
|
||||
if len(o.PreferredModel) > 0 {
|
||||
spec := &api.LLMSpec{
|
||||
Ollama: nil,
|
||||
Vllm: &api.LLMSpecVllm{
|
||||
PreferredModel: o.PreferredModel,
|
||||
},
|
||||
Dify: nil,
|
||||
}
|
||||
dict.Set("llm_spec", jsonutils.Marshal(spec))
|
||||
}
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user