mirror of
https://github.com/langgenius/dify-plugin-daemon.git
synced 2026-07-22 09:45:27 -04:00
375 lines
14 KiB
Go
375 lines
14 KiB
Go
package plugin_entities
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"time"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/entities/constants"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/entities/manifest_entities"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/utils/parser"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/validators"
|
|
)
|
|
|
|
type PluginCategory string
|
|
|
|
const (
|
|
PLUGIN_CATEGORY_TOOL PluginCategory = "tool"
|
|
PLUGIN_CATEGORY_MODEL PluginCategory = "model"
|
|
PLUGIN_CATEGORY_EXTENSION PluginCategory = "extension"
|
|
PLUGIN_CATEGORY_AGENT_STRATEGY PluginCategory = "agent-strategy"
|
|
PLUGIN_CATEGORY_DATASOURCE PluginCategory = "datasource"
|
|
PLUGIN_CATEGORY_TRIGGER PluginCategory = "trigger"
|
|
)
|
|
|
|
type PluginPermissionRequirement struct {
|
|
Tool *PluginPermissionToolRequirement `json:"tool,omitempty" yaml:"tool,omitempty" validate:"omitempty"`
|
|
Model *PluginPermissionModelRequirement `json:"model,omitempty" yaml:"model,omitempty" validate:"omitempty"`
|
|
Node *PluginPermissionNodeRequirement `json:"node,omitempty" yaml:"node,omitempty" validate:"omitempty"`
|
|
Endpoint *PluginPermissionEndpointRequirement `json:"endpoint,omitempty" yaml:"endpoint,omitempty" validate:"omitempty"`
|
|
App *PluginPermissionAppRequirement `json:"app,omitempty" yaml:"app,omitempty" validate:"omitempty"`
|
|
Storage *PluginPermissionStorageRequirement `json:"storage,omitempty" yaml:"storage,omitempty" validate:"omitempty"`
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeTool() bool {
|
|
return p != nil && p.Tool != nil && p.Tool.Enabled
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeModel() bool {
|
|
return p != nil && p.Model != nil && p.Model.Enabled
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeLLM() bool {
|
|
return p != nil && p.Model != nil && p.Model.Enabled && p.Model.LLM
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeTextEmbedding() bool {
|
|
return p != nil && p.Model != nil && p.Model.Enabled && p.Model.TextEmbedding
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeRerank() bool {
|
|
return p != nil && p.Model != nil && p.Model.Enabled && p.Model.Rerank
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeTTS() bool {
|
|
return p != nil && p.Model != nil && p.Model.Enabled && p.Model.TTS
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeSpeech2Text() bool {
|
|
return p != nil && p.Model != nil && p.Model.Enabled && p.Model.Speech2text
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeModeration() bool {
|
|
return p != nil && p.Model != nil && p.Model.Enabled && p.Model.Moderation
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeNode() bool {
|
|
return p != nil && p.Node != nil && p.Node.Enabled
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeApp() bool {
|
|
return p != nil && p.App != nil && p.App.Enabled
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowRegisterEndpoint() bool {
|
|
return p != nil && p.Endpoint != nil && p.Endpoint.Enabled
|
|
}
|
|
|
|
func (p *PluginPermissionRequirement) AllowInvokeStorage() bool {
|
|
return p != nil && p.Storage != nil && p.Storage.Enabled
|
|
}
|
|
|
|
type PluginPermissionToolRequirement struct {
|
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
}
|
|
|
|
type PluginPermissionModelRequirement struct {
|
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
LLM bool `json:"llm" yaml:"llm"`
|
|
TextEmbedding bool `json:"text_embedding" yaml:"text_embedding"`
|
|
Rerank bool `json:"rerank" yaml:"rerank"`
|
|
TTS bool `json:"tts" yaml:"tts"`
|
|
Speech2text bool `json:"speech2text" yaml:"speech2text"`
|
|
Moderation bool `json:"moderation" yaml:"moderation"`
|
|
}
|
|
|
|
type PluginPermissionNodeRequirement struct {
|
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
}
|
|
|
|
type PluginPermissionEndpointRequirement struct {
|
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
}
|
|
|
|
type PluginPermissionAppRequirement struct {
|
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
}
|
|
|
|
type PluginPermissionStorageRequirement struct {
|
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
Size uint64 `json:"size" yaml:"size" validate:"min=1024,max=1073741824"` // min 1024 bytes, max 1G
|
|
}
|
|
|
|
type PluginResourceRequirement struct {
|
|
// Memory in bytes
|
|
Memory int64 `json:"memory" yaml:"memory" validate:"required"`
|
|
// Permission requirements
|
|
Permission *PluginPermissionRequirement `json:"permission,omitempty" yaml:"permission,omitempty" validate:"omitempty"`
|
|
}
|
|
|
|
type PluginDeclarationPlatformArch string
|
|
|
|
type PluginRunner struct {
|
|
Language constants.Language `json:"language" yaml:"language" validate:"required,is_available_language"`
|
|
Version string `json:"version" yaml:"version" validate:"required,max=128"`
|
|
Entrypoint string `json:"entrypoint" yaml:"entrypoint" validate:"required,max=256"`
|
|
}
|
|
|
|
type PluginMeta struct {
|
|
Version string `json:"version" yaml:"version" validate:"required,version"`
|
|
Arch []constants.Arch `json:"arch" yaml:"arch" validate:"required,dive,is_available_arch"`
|
|
Runner PluginRunner `json:"runner" yaml:"runner" validate:"required"`
|
|
MinimumDifyVersion *string `json:"minimum_dify_version" yaml:"minimum_dify_version"`
|
|
}
|
|
|
|
type PluginExtensions struct {
|
|
Tools []string `json:"tools" yaml:"tools,omitempty" validate:"omitempty,dive,max=128"`
|
|
Models []string `json:"models" yaml:"models,omitempty" validate:"omitempty,dive,max=128"`
|
|
Endpoints []string `json:"endpoints" yaml:"endpoints,omitempty" validate:"omitempty,dive,max=128"`
|
|
AgentStrategies []string `json:"agent_strategies" yaml:"agent_strategies,omitempty" validate:"omitempty,dive,max=128"`
|
|
Datasources []string `json:"datasources" yaml:"datasources,omitempty" validate:"omitempty,dive,max=128"`
|
|
Triggers []string `json:"triggers" yaml:"triggers,omitempty" validate:"omitempty,dive,max=128"`
|
|
}
|
|
|
|
type PluginDeclarationWithoutAdvancedFields struct {
|
|
Version manifest_entities.Version `json:"version" yaml:"version,omitempty" validate:"required,version"`
|
|
Type manifest_entities.DifyManifestType `json:"type" yaml:"type,omitempty" validate:"required,eq=plugin"`
|
|
Author string `json:"author" yaml:"author,omitempty" validate:"omitempty,max=64"`
|
|
Name string `json:"name" yaml:"name,omitempty" validate:"required,max=128"`
|
|
Label I18nObject `json:"label" yaml:"label" validate:"required"`
|
|
Description I18nObject `json:"description" yaml:"description" validate:"required"`
|
|
Icon string `json:"icon" yaml:"icon,omitempty" validate:"required,max=128"`
|
|
IconDark string `json:"icon_dark" yaml:"icon_dark,omitempty" validate:"omitempty,max=128"`
|
|
Resource PluginResourceRequirement `json:"resource" yaml:"resource,omitempty" validate:"required"`
|
|
Plugins PluginExtensions `json:"plugins" yaml:"plugins,omitempty" validate:"required"`
|
|
Meta PluginMeta `json:"meta" yaml:"meta,omitempty" validate:"required"`
|
|
Tags []manifest_entities.PluginTag `json:"tags" yaml:"tags,omitempty" validate:"omitempty,dive,plugin_tag,max=128"`
|
|
CreatedAt time.Time `json:"created_at" yaml:"created_at,omitempty" validate:"required"`
|
|
Privacy *string `json:"privacy,omitempty" yaml:"privacy,omitempty" validate:"omitempty"`
|
|
Repo *string `json:"repo,omitempty" yaml:"repo,omitempty" validate:"omitempty,url"`
|
|
}
|
|
|
|
func (p *PluginDeclarationWithoutAdvancedFields) UnmarshalJSON(data []byte) error {
|
|
type Alias PluginDeclarationWithoutAdvancedFields
|
|
aux := &struct {
|
|
*Alias
|
|
}{
|
|
Alias: (*Alias)(p),
|
|
}
|
|
|
|
if err := json.Unmarshal(data, aux); err != nil {
|
|
return err
|
|
}
|
|
|
|
if p.Tags == nil {
|
|
p.Tags = []manifest_entities.PluginTag{}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type PluginDeclaration struct {
|
|
PluginDeclarationWithoutAdvancedFields `yaml:",inline"`
|
|
Verified bool `json:"verified" yaml:"verified"`
|
|
Endpoint *EndpointProviderDeclaration `json:"endpoint,omitempty" yaml:"endpoint,omitempty" validate:"omitempty"`
|
|
Model *ModelProviderDeclaration `json:"model,omitempty" yaml:"model,omitempty" validate:"omitempty"`
|
|
Tool *ToolProviderDeclaration `json:"tool,omitempty" yaml:"tool,omitempty" validate:"omitempty"`
|
|
AgentStrategy *AgentStrategyProviderDeclaration `json:"agent_strategy,omitempty" yaml:"agent_strategy,omitempty" validate:"omitempty"`
|
|
Datasource *DatasourceProviderDeclaration `json:"datasource,omitempty" yaml:"datasource,omitempty" validate:"omitempty"`
|
|
Trigger *TriggerProviderDeclaration `json:"trigger,omitempty" yaml:"trigger,omitempty" validate:"omitempty"`
|
|
}
|
|
|
|
func (p *PluginDeclaration) Category() PluginCategory {
|
|
if p.Tool != nil || len(p.Plugins.Tools) != 0 {
|
|
return PLUGIN_CATEGORY_TOOL
|
|
}
|
|
if p.Model != nil || len(p.Plugins.Models) != 0 {
|
|
return PLUGIN_CATEGORY_MODEL
|
|
}
|
|
if p.Datasource != nil || len(p.Plugins.Datasources) != 0 {
|
|
return PLUGIN_CATEGORY_DATASOURCE
|
|
}
|
|
if p.AgentStrategy != nil || len(p.Plugins.AgentStrategies) != 0 {
|
|
return PLUGIN_CATEGORY_AGENT_STRATEGY
|
|
}
|
|
if p.Trigger != nil || len(p.Plugins.Triggers) != 0 {
|
|
return PLUGIN_CATEGORY_TRIGGER
|
|
}
|
|
return PLUGIN_CATEGORY_EXTENSION
|
|
}
|
|
|
|
func (p *PluginDeclaration) UnmarshalJSON(data []byte) error {
|
|
// First unmarshal the embedded struct
|
|
if err := json.Unmarshal(data, &p.PluginDeclarationWithoutAdvancedFields); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Then unmarshal the remaining fields
|
|
type PluginExtra struct {
|
|
Verified bool `json:"verified"`
|
|
Endpoint *EndpointProviderDeclaration `json:"endpoint,omitempty"`
|
|
Model *ModelProviderDeclaration `json:"model,omitempty"`
|
|
Tool *ToolProviderDeclaration `json:"tool,omitempty"`
|
|
AgentStrategy *AgentStrategyProviderDeclaration `json:"agent_strategy,omitempty"`
|
|
Datasource *DatasourceProviderDeclaration `json:"datasource,omitempty"`
|
|
Trigger *TriggerProviderDeclaration `json:"trigger,omitempty"`
|
|
}
|
|
|
|
var extra PluginExtra
|
|
if err := json.Unmarshal(data, &extra); err != nil {
|
|
return err
|
|
}
|
|
|
|
p.Verified = extra.Verified
|
|
p.Endpoint = extra.Endpoint
|
|
p.Model = extra.Model
|
|
p.Tool = extra.Tool
|
|
p.AgentStrategy = extra.AgentStrategy
|
|
p.Datasource = extra.Datasource
|
|
p.Trigger = extra.Trigger
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *PluginDeclaration) MarshalJSON() ([]byte, error) {
|
|
// TODO: performance issue, need a better way to do this
|
|
c := *p
|
|
c.FillInDefaultValues()
|
|
type alias PluginDeclaration
|
|
return json.Marshal(alias(c))
|
|
}
|
|
|
|
var (
|
|
PluginNameRegex = regexp.MustCompile(`^[a-z0-9_-]{1,128}$`)
|
|
AuthorRegex = regexp.MustCompile(`^[a-z0-9_-]{1,64}$`)
|
|
)
|
|
|
|
func isPluginName(fl validator.FieldLevel) bool {
|
|
value := fl.Field().String()
|
|
return PluginNameRegex.MatchString(value)
|
|
}
|
|
|
|
func (p *PluginDeclaration) Identity() string {
|
|
return parser.MarshalPluginID(p.Author, p.Name, p.Version.String())
|
|
}
|
|
|
|
func (p *PluginDeclaration) ManifestValidate() error {
|
|
if !AuthorRegex.MatchString(p.Author) {
|
|
return fmt.Errorf("author must be alphanumeric and less than 64 characters: ^[a-z0-9_-]{1,64}$")
|
|
}
|
|
|
|
if !PluginNameRegex.MatchString(p.Name) {
|
|
return fmt.Errorf("plugin name must be alphanumeric and less than 128 characters: ^[a-z0-9_-]{1,128}$")
|
|
}
|
|
|
|
if p.Endpoint == nil && p.Model == nil && p.Tool == nil && p.AgentStrategy == nil && p.Datasource == nil && p.Trigger == nil {
|
|
return fmt.Errorf("at least one of endpoint, model, tool, agent_strategy, trigger, or datasource must be provided")
|
|
}
|
|
|
|
if p.Model != nil {
|
|
if p.Datasource != nil || p.Tool != nil || p.Endpoint != nil || p.AgentStrategy != nil || p.Trigger != nil {
|
|
return fmt.Errorf("model and datasource, tool, endpoint, trigger, or agent_strategy cannot be provided at the same time")
|
|
}
|
|
}
|
|
|
|
if p.AgentStrategy != nil {
|
|
if p.Tool != nil || p.Model != nil || p.Endpoint != nil || p.Datasource != nil || p.Trigger != nil {
|
|
return fmt.Errorf("agent_strategy and tool, model, endpoint, trigger, or datasource cannot be provided at the same time")
|
|
}
|
|
}
|
|
|
|
if p.Datasource != nil {
|
|
if p.Tool != nil || p.Model != nil || p.Endpoint != nil || p.AgentStrategy != nil || p.Trigger != nil {
|
|
return fmt.Errorf("datasource and tool, model, endpoint, trigger, or agent_strategy cannot be provided at the same time")
|
|
}
|
|
}
|
|
|
|
if p.Trigger != nil {
|
|
if p.Tool != nil || p.Model != nil || p.Endpoint != nil || p.AgentStrategy != nil || p.Datasource != nil {
|
|
return fmt.Errorf("trigger and tool, model, endpoint, or agent_strategy cannot be provided at the same time")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *PluginDeclaration) FillInDefaultValues() {
|
|
if p.Tool != nil {
|
|
if p.Tool.Identity.Description.EnUS == "" {
|
|
p.Tool.Identity.Description = p.Description
|
|
}
|
|
|
|
if len(p.Tool.Identity.Tags) == 0 {
|
|
p.Tool.Identity.Tags = p.Tags
|
|
}
|
|
}
|
|
|
|
if p.Model != nil {
|
|
if p.Model.Description == nil {
|
|
deepCopiedDescription := p.Description
|
|
p.Model.Description = &deepCopiedDescription
|
|
}
|
|
}
|
|
|
|
if p.Trigger != nil {
|
|
if p.Trigger.Identity.Description.EnUS == "" {
|
|
p.Trigger.Identity.Description = p.Description
|
|
}
|
|
|
|
if len(p.Trigger.Identity.Tags) == 0 {
|
|
p.Trigger.Identity.Tags = p.Tags
|
|
}
|
|
}
|
|
|
|
if p.Tags == nil {
|
|
p.Tags = []manifest_entities.PluginTag{}
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
// init validator
|
|
validators.GlobalEntitiesValidator.RegisterValidation("plugin_name", isPluginName)
|
|
}
|
|
|
|
func UnmarshalPluginDeclarationFromYaml(data []byte) (*PluginDeclaration, error) {
|
|
obj, err := parser.UnmarshalYamlBytes[PluginDeclaration](data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := validators.GlobalEntitiesValidator.Struct(obj); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
obj.FillInDefaultValues()
|
|
|
|
return &obj, nil
|
|
}
|
|
|
|
func UnmarshalPluginDeclarationFromJSON(data []byte) (*PluginDeclaration, error) {
|
|
obj, err := parser.UnmarshalJsonBytes[PluginDeclaration](data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := validators.GlobalEntitiesValidator.Struct(obj); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
obj.FillInDefaultValues()
|
|
|
|
return &obj, nil
|
|
}
|