Переглянути джерело

feat: add detailed decoder

Yeuoly 11 місяців тому
батько
коміт
4f6467b6f9

+ 0 - 13
internal/core/plugin_manager/init.go

@@ -1,13 +0,0 @@
-package plugin_manager
-
-import (
-	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
-)
-
-func (m *PluginManager) checkPluginExist(identity string) (plugin_entities.PluginRuntimeInterface, bool) {
-	if v, ok := m.m.Load(identity); ok {
-		return v.(plugin_entities.PluginRuntimeInterface), true
-	}
-
-	return nil, false
-}

+ 66 - 0
internal/core/plugin_packager/decoder/decoder.go

@@ -1,10 +1,13 @@
 package decoder
 
 import (
+	"errors"
+	"fmt"
 	"io"
 	"io/fs"
 
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
 )
 
 // PluginDecoder is an interface for decoding and interacting with plugin files
@@ -38,3 +41,66 @@ type PluginDecoder interface {
 	// Manifest returns the manifest of the plugin
 	Manifest() (plugin_entities.PluginDeclaration, error)
 }
+
+type PluginDecoderHelper struct {
+	pluginDeclaration *plugin_entities.PluginDeclaration
+}
+
+func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.PluginDeclaration, error) {
+	if p.pluginDeclaration != nil {
+		return *p.pluginDeclaration, nil
+	}
+
+	// read the manifest file
+	manifest, err := decoder.ReadFile("manifest.yaml")
+	if err != nil {
+		return plugin_entities.PluginDeclaration{}, err
+	}
+
+	dec, err := parser.UnmarshalYamlBytes[plugin_entities.PluginDeclaration](manifest)
+	if err != nil {
+		return plugin_entities.PluginDeclaration{}, err
+	}
+
+	// try to load plugins
+	plugins := dec.Plugins
+	for _, plugin := range plugins {
+		// read yaml
+		plugin_yaml, err := decoder.ReadFile(plugin)
+		if err != nil {
+			return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read plugin file: %s", plugin))
+		}
+
+		plugin_dec, err := parser.UnmarshalYamlBytes[plugin_entities.GenericProviderDeclaration](plugin_yaml)
+		if err != nil {
+			return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal plugin file: %s", plugin))
+		}
+
+		switch plugin_dec.Type {
+		case plugin_entities.PROVIDER_TYPE_ENDPOINT:
+			dec.Endpoint, err = parser.MapToStruct[plugin_entities.EndpointProviderDeclaration](plugin_dec.Provider)
+			if err != nil {
+				return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to convert endpoint to struct: %s", plugin))
+			}
+		case plugin_entities.PROVIDER_TYPE_TOOL:
+			dec.Tool, err = parser.MapToStruct[plugin_entities.ToolProviderConfiguration](plugin_dec.Provider)
+			if err != nil {
+				return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to convert tool to struct: %s", plugin))
+			}
+		case plugin_entities.PROVIDER_TYPE_MODEL:
+			dec.Model, err = parser.MapToStruct[plugin_entities.ModelProviderConfiguration](plugin_dec.Provider)
+			if err != nil {
+				return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to convert model to struct: %s", plugin))
+			}
+		default:
+			return plugin_entities.PluginDeclaration{}, fmt.Errorf("unknown provider type: %s", plugin_dec.Type)
+		}
+	}
+
+	if err := dec.ManifestValidate(); err != nil {
+		return plugin_entities.PluginDeclaration{}, err
+	}
+
+	p.pluginDeclaration = &dec
+	return dec, nil
+}

+ 2 - 20
internal/core/plugin_packager/decoder/fs.go

@@ -9,7 +9,6 @@ import (
 	"strings"
 
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
-	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
 )
 
 var (
@@ -18,13 +17,12 @@ var (
 
 type FSPluginDecoder struct {
 	PluginDecoder
+	PluginDecoderHelper
 
 	// root directory of the plugin
 	root string
 
 	fs fs.FS
-
-	pluginDeclaration *plugin_entities.PluginDeclaration
 }
 
 func NewFSPluginDecoder(root string) (*FSPluginDecoder, error) {
@@ -105,21 +103,5 @@ func (d *FSPluginDecoder) CreateTime() (int64, error) {
 }
 
 func (d *FSPluginDecoder) Manifest() (plugin_entities.PluginDeclaration, error) {
-	if d.pluginDeclaration != nil {
-		return *d.pluginDeclaration, nil
-	}
-
-	// read the manifest file
-	manifest, err := d.ReadFile("manifest.yaml")
-	if err != nil {
-		return plugin_entities.PluginDeclaration{}, err
-	}
-
-	dec, err := parser.UnmarshalYamlBytes[plugin_entities.PluginDeclaration](manifest)
-	if err != nil {
-		return plugin_entities.PluginDeclaration{}, err
-	}
-
-	d.pluginDeclaration = &dec
-	return dec, nil
+	return d.PluginDecoderHelper.Manifest(d)
 }

+ 2 - 17
internal/core/plugin_packager/decoder/zip.go

@@ -13,6 +13,7 @@ import (
 
 type ZipPluginDecoder struct {
 	PluginDecoder
+	PluginDecoderHelper
 
 	reader *zip.Reader
 	err    error
@@ -164,21 +165,5 @@ func (z *ZipPluginDecoder) CreateTime() (int64, error) {
 }
 
 func (z *ZipPluginDecoder) Manifest() (plugin_entities.PluginDeclaration, error) {
-	if z.pluginDeclaration != nil {
-		return *z.pluginDeclaration, nil
-	}
-
-	// read the manifest file
-	manifest, err := z.ReadFile("manifest.yaml")
-	if err != nil {
-		return plugin_entities.PluginDeclaration{}, err
-	}
-
-	dec, err := parser.UnmarshalYamlBytes[plugin_entities.PluginDeclaration](manifest)
-	if err != nil {
-		return plugin_entities.PluginDeclaration{}, err
-	}
-
-	z.pluginDeclaration = &dec
-	return dec, nil
+	return z.PluginDecoderHelper.Manifest(z)
 }

+ 20 - 3
internal/types/entities/plugin_entities/plugin_declaration.go

@@ -1,6 +1,7 @@
 package plugin_entities
 
 import (
+	"fmt"
 	"regexp"
 	"time"
 
@@ -137,8 +138,8 @@ type PluginDeclaration struct {
 	Execution PluginExecution              `json:"execution" yaml:"execution" validate:"required"`
 	Meta      PluginMeta                   `json:"meta" yaml:"meta" validate:"required"`
 	Endpoint  *EndpointProviderDeclaration `json:"endpoints" validate:"omitempty"`
-	Models    *ModelProviderConfiguration  `json:"models" validate:"omitempty"`
-	Tools     *ToolProviderConfiguration   `json:"tools" validate:"omitempty"`
+	Model     *ModelProviderConfiguration  `json:"models" validate:"omitempty"`
+	Tool      *ToolProviderConfiguration   `json:"tools" validate:"omitempty"`
 }
 
 var (
@@ -146,7 +147,7 @@ var (
 )
 
 func isVersion(fl validator.FieldLevel) bool {
-	// version format must be like x.x.x, at least 2 digits and most 5 digits, can be ends with a letter
+	// version format must be like x.x.x, at least 2 digits and most 5 digits, and it can be ends with a letter
 	value := fl.Field().String()
 	return plugin_declaration_version_regex.MatchString(value)
 }
@@ -155,6 +156,22 @@ func (p *PluginDeclaration) Identity() string {
 	return parser.MarshalPluginIdentity(p.Name, p.Version)
 }
 
+func (p *PluginDeclaration) ManifestValidate() error {
+	if p.Endpoint == nil && p.Model == nil && p.Tool == nil {
+		return fmt.Errorf("at least one of endpoint, model, or tool must be provided")
+	}
+
+	if p.Model != nil && p.Tool != nil {
+		return fmt.Errorf("model and tool cannot be provided at the same time")
+	}
+
+	if p.Model != nil && p.Endpoint != nil {
+		return fmt.Errorf("model and endpoint cannot be provided at the same time")
+	}
+
+	return nil
+}
+
 func init() {
 	// init validator
 	validators.GlobalEntitiesValidator.RegisterValidation("version", isVersion)

+ 24 - 0
internal/types/entities/plugin_entities/provider_declaration.go

@@ -1,4 +1,28 @@
 package plugin_entities
 
+import (
+	"github.com/go-playground/validator/v10"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/validators"
+)
+
+type ProviderType string
+
+const (
+	PROVIDER_TYPE_MODEL    ProviderType = "model"
+	PROVIDER_TYPE_TOOL     ProviderType = "tool"
+	PROVIDER_TYPE_ENDPOINT ProviderType = "endpoint"
+)
+
+func isAvailableProviderType(fl validator.FieldLevel) bool {
+	str := fl.Field().String()
+	return str == string(PROVIDER_TYPE_MODEL) || str == string(PROVIDER_TYPE_TOOL) || str == string(PROVIDER_TYPE_ENDPOINT)
+}
+
+func init() {
+	validators.GlobalEntitiesValidator.RegisterValidation("provider_type", isAvailableProviderType)
+}
+
 type GenericProviderDeclaration struct {
+	Type     ProviderType   `json:"type" yaml:"type" validate:"required,provider_type"`
+	Provider map[string]any `json:"provider" yaml:"provider" validate:"required"`
 }