Explorar o código

feat: backwards invoke to nodes

Yeuoly hai 10 meses
pai
achega
b69398ce4b

+ 4 - 0
internal/core/dify_invocation/invcation.go

@@ -23,6 +23,10 @@ type BackwardsInvocation interface {
 	InvokeTool(payload *InvokeToolRequest) (*stream.Stream[tool_entities.ToolResponseChunk], error)
 	// InvokeApp
 	InvokeApp(payload *InvokeAppRequest) (*stream.Stream[map[string]any], error)
+	// InvokeParameterExtractor
+	InvokeParameterExtractor(payload *InvokeParameterExtractorRequest) (*InvokeNodeResponse, error)
+	// InvokeQuestionClassifier
+	InvokeQuestionClassifier(payload *InvokeQuestionClassifierRequest) (*InvokeNodeResponse, error)
 	// InvokeEncrypt
 	InvokeEncrypt(payload *InvokeEncryptRequest) (map[string]any, error)
 }

+ 21 - 1
internal/core/dify_invocation/real/http_request.go

@@ -1,9 +1,12 @@
 package real
 
 import (
+	"fmt"
+
 	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/model_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/tool_entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/validators"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/http_requests"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/stream"
 )
@@ -17,7 +20,16 @@ func Request[T any](i *RealBackwardsInvocation, method string, path string, opti
 		http_requests.HttpReadTimeout(240000),
 	)
 
-	return http_requests.RequestAndParse[T](i.client, i.difyPath(path), method, options...)
+	req, err := http_requests.RequestAndParse[T](i.client, i.difyPath(path), method, options...)
+	if err != nil {
+		return nil, err
+	}
+
+	if err := validators.GlobalEntitiesValidator.Struct(req); err != nil {
+		return nil, fmt.Errorf("validate request failed: %s", err.Error())
+	}
+
+	return req, nil
 }
 
 func StreamResponse[T any](i *RealBackwardsInvocation, method string, path string, options ...http_requests.HttpOptions) (*stream.Stream[T], error) {
@@ -64,6 +76,14 @@ func (i *RealBackwardsInvocation) InvokeApp(payload *dify_invocation.InvokeAppRe
 	return StreamResponse[map[string]any](i, "POST", "invoke/app", http_requests.HttpPayloadJson(payload))
 }
 
+func (i *RealBackwardsInvocation) InvokeParameterExtractor(payload *dify_invocation.InvokeParameterExtractorRequest) (*dify_invocation.InvokeNodeResponse, error) {
+	return Request[dify_invocation.InvokeNodeResponse](i, "POST", "invoke/parameter-extractor", http_requests.HttpPayloadJson(payload))
+}
+
+func (i *RealBackwardsInvocation) InvokeQuestionClassifier(payload *dify_invocation.InvokeQuestionClassifierRequest) (*dify_invocation.InvokeNodeResponse, error) {
+	return Request[dify_invocation.InvokeNodeResponse](i, "POST", "invoke/question-classifier", http_requests.HttpPayloadJson(payload))
+}
+
 func (i *RealBackwardsInvocation) InvokeEncrypt(payload *dify_invocation.InvokeEncryptRequest) (map[string]any, error) {
 	if !payload.EncryptRequired(payload.Data) {
 		return payload.Data, nil

+ 16 - 0
internal/core/dify_invocation/tester/mock.go

@@ -190,3 +190,19 @@ func (m *MockedDifyInvocation) InvokeApp(payload *dify_invocation.InvokeAppReque
 func (m *MockedDifyInvocation) InvokeEncrypt(payload *dify_invocation.InvokeEncryptRequest) (map[string]any, error) {
 	return payload.Data, nil
 }
+
+func (m *MockedDifyInvocation) InvokeParameterExtractor(payload *dify_invocation.InvokeParameterExtractorRequest) (*dify_invocation.InvokeNodeResponse, error) {
+	return &dify_invocation.InvokeNodeResponse{
+		ProcessData: map[string]any{},
+		Outputs:     map[string]any{},
+		Inputs:      map[string]any{},
+	}, nil
+}
+
+func (m *MockedDifyInvocation) InvokeQuestionClassifier(payload *dify_invocation.InvokeQuestionClassifierRequest) (*dify_invocation.InvokeNodeResponse, error) {
+	return &dify_invocation.InvokeNodeResponse{
+		ProcessData: map[string]any{},
+		Outputs:     map[string]any{},
+		Inputs:      map[string]any{},
+	}, nil
+}

+ 51 - 15
internal/core/dify_invocation/types.go

@@ -17,17 +17,18 @@ type BaseInvokeDifyRequest struct {
 type InvokeType string
 
 const (
-	INVOKE_TYPE_LLM            InvokeType = "llm"
-	INVOKE_TYPE_TEXT_EMBEDDING InvokeType = "text_embedding"
-	INVOKE_TYPE_RERANK         InvokeType = "rerank"
-	INVOKE_TYPE_TTS            InvokeType = "tts"
-	INVOKE_TYPE_SPEECH2TEXT    InvokeType = "speech2text"
-	INVOKE_TYPE_MODERATION     InvokeType = "moderation"
-	INVOKE_TYPE_TOOL           InvokeType = "tool"
-	INVOKE_TYPE_NODE           InvokeType = "node"
-	INVOKE_TYPE_APP            InvokeType = "app"
-	INVOKE_TYPE_STORAGE        InvokeType = "storage"
-	INVOKE_TYPE_ENCRYPT        InvokeType = "encrypt"
+	INVOKE_TYPE_LLM                      InvokeType = "llm"
+	INVOKE_TYPE_TEXT_EMBEDDING           InvokeType = "text_embedding"
+	INVOKE_TYPE_RERANK                   InvokeType = "rerank"
+	INVOKE_TYPE_TTS                      InvokeType = "tts"
+	INVOKE_TYPE_SPEECH2TEXT              InvokeType = "speech2text"
+	INVOKE_TYPE_MODERATION               InvokeType = "moderation"
+	INVOKE_TYPE_TOOL                     InvokeType = "tool"
+	INVOKE_TYPE_NODE_PARAMETER_EXTRACTOR InvokeType = "node_parameter_extractor"
+	INVOKE_TYPE_NODE_QUESTION_CLASSIFIER InvokeType = "node_question_classifier"
+	INVOKE_TYPE_APP                      InvokeType = "app"
+	INVOKE_TYPE_STORAGE                  InvokeType = "storage"
+	INVOKE_TYPE_ENCRYPT                  InvokeType = "encrypt"
 )
 
 type InvokeLLMRequest struct {
@@ -105,6 +106,42 @@ type InvokeAppRequest struct {
 	InvokeAppSchema
 }
 
+type ModelConfig struct {
+	Provider         string         `json:"provider" validate:"required"`
+	Name             string         `json:"name" validate:"required"`
+	Mode             string         `json:"mode" validate:"required"`
+	CompletionParams map[string]any `json:"completion_params" validate:"omitempty"`
+}
+
+type InvokeParameterExtractorRequest struct {
+	BaseInvokeDifyRequest
+
+	Parameters []struct {
+		Name        string   `json:"name" validate:"required"`
+		Type        string   `json:"type" validate:"required,oneof=string number bool select array[string] array[number] array[object]"`
+		Options     []string `json:"options" validate:"omitempty"`
+		Description string   `json:"description" validate:"omitempty"`
+		Required    bool     `json:"required" validate:"omitempty"`
+	} `json:"parameters" validate:"required,dive"`
+
+	Model       ModelConfig `json:"model" validate:"required"`
+	Instruction string      `json:"instruction" validate:"omitempty"`
+	Query       string      `json:"query" validate:"required"`
+}
+
+type InvokeQuestionClassifierRequest struct {
+	BaseInvokeDifyRequest
+
+	Classes []struct {
+		ID   string `json:"id" validate:"required"`
+		Name string `json:"name" validate:"required"`
+	} `json:"classes" validate:"required,dive"`
+
+	Model       ModelConfig `json:"model" validate:"required"`
+	Instruction string      `json:"instruction" validate:"omitempty"`
+	Query       string      `json:"query" validate:"required"`
+}
+
 type EncryptOpt string
 
 const (
@@ -171,8 +208,7 @@ type InvokeToolRequest struct {
 }
 
 type InvokeNodeResponse struct {
-	ProcessData      map[string]any `json:"process_data"`
-	Output           map[string]any `json:"output"`
-	Input            map[string]any `json:"input"`
-	EdgeSourceHandle []string       `json:"edge_source_handle"`
+	ProcessData map[string]any `json:"process_data" validate:"required"`
+	Outputs     map[string]any `json:"outputs" validate:"required"`
+	Inputs      map[string]any `json:"inputs" validate:"required"`
 }

+ 39 - 1
internal/core/plugin_daemon/backwards_invocation/task.go

@@ -109,7 +109,13 @@ var (
 			},
 			"error": "permission denied, you need to enable moderation access in plugin manifest",
 		},
-		dify_invocation.INVOKE_TYPE_NODE: {
+		dify_invocation.INVOKE_TYPE_NODE_PARAMETER_EXTRACTOR: {
+			"func": func(declaration *plugin_entities.PluginDeclaration) bool {
+				return declaration.Resource.Permission.AllowInvokeNode()
+			},
+			"error": "permission denied, you need to enable node access in plugin manifest",
+		},
+		dify_invocation.INVOKE_TYPE_NODE_QUESTION_CLASSIFIER: {
 			"func": func(declaration *plugin_entities.PluginDeclaration) bool {
 				return declaration.Resource.Permission.AllowInvokeNode()
 			},
@@ -205,6 +211,12 @@ var (
 		dify_invocation.INVOKE_TYPE_APP: func(handle *BackwardsInvocation) {
 			genericDispatchTask(handle, executeDifyInvocationAppTask)
 		},
+		dify_invocation.INVOKE_TYPE_NODE_PARAMETER_EXTRACTOR: func(handle *BackwardsInvocation) {
+			genericDispatchTask(handle, executeDifyInvocationParameterExtractor)
+		},
+		dify_invocation.INVOKE_TYPE_NODE_QUESTION_CLASSIFIER: func(handle *BackwardsInvocation) {
+			genericDispatchTask(handle, executeDifyInvocationQuestionClassifier)
+		},
 		dify_invocation.INVOKE_TYPE_STORAGE: func(handle *BackwardsInvocation) {
 			genericDispatchTask(handle, executeDifyInvocationStorageTask)
 		},
@@ -373,6 +385,32 @@ func executeDifyInvocationAppTask(
 	})
 }
 
+func executeDifyInvocationParameterExtractor(
+	handle *BackwardsInvocation,
+	request *dify_invocation.InvokeParameterExtractorRequest,
+) {
+	response, err := handle.backwardsInvocation.InvokeParameterExtractor(request)
+	if err != nil {
+		handle.WriteError(fmt.Errorf("invoke parameter extractor failed: %s", err.Error()))
+		return
+	}
+
+	handle.WriteResponse("struct", response)
+}
+
+func executeDifyInvocationQuestionClassifier(
+	handle *BackwardsInvocation,
+	request *dify_invocation.InvokeQuestionClassifierRequest,
+) {
+	response, err := handle.backwardsInvocation.InvokeQuestionClassifier(request)
+	if err != nil {
+		handle.WriteError(fmt.Errorf("invoke question classifier failed: %s", err.Error()))
+		return
+	}
+
+	handle.WriteResponse("struct", response)
+}
+
 func executeDifyInvocationStorageTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeStorageRequest,

+ 13 - 33
internal/core/plugin_daemon/backwards_invocation/task_test.go

@@ -6,40 +6,10 @@ import (
 	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation/tester"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_daemon/access_types"
-	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_manager/positive_manager"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/session_manager"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 )
 
-type TPluginRuntime struct {
-	plugin_entities.PluginRuntime
-	positive_manager.PositivePluginRuntime
-}
-
-func (r *TPluginRuntime) InitEnvironment() error {
-	return nil
-}
-
-func (r *TPluginRuntime) Checksum() string {
-	return ""
-}
-
-func (r *TPluginRuntime) Identity() (string, error) {
-	return "", nil
-}
-
-func (r *TPluginRuntime) StartPlugin() error {
-	return nil
-}
-
-func (r *TPluginRuntime) Type() plugin_entities.PluginRuntimeType {
-	return plugin_entities.PLUGIN_RUNTIME_TYPE_LOCAL
-}
-
-func (r *TPluginRuntime) Wait() (<-chan bool, error) {
-	return nil, nil
-}
-
 func getTestSession() *session_manager.Session {
 	return session_manager.NewSession(
 		session_manager.NewSessionPayload{
@@ -125,8 +95,13 @@ func TestBackwardsInvocationAllPermittedPermission(t *testing.T) {
 		t.Errorf("checkPermission failed: %s", err.Error())
 	}
 
-	invoke_node_request := NewBackwardsInvocation(dify_invocation.INVOKE_TYPE_NODE, "", getTestSession(), nil, nil)
-	if err := checkPermission(&all_permitted_runtime, invoke_node_request); err != nil {
+	invoke_node_parameter_extractor_request := NewBackwardsInvocation(dify_invocation.INVOKE_TYPE_NODE_PARAMETER_EXTRACTOR, "", getTestSession(), nil, nil)
+	if err := checkPermission(&all_permitted_runtime, invoke_node_parameter_extractor_request); err != nil {
+		t.Errorf("checkPermission failed: %s", err.Error())
+	}
+
+	invoke_node_question_classifier_request := NewBackwardsInvocation(dify_invocation.INVOKE_TYPE_NODE_QUESTION_CLASSIFIER, "", getTestSession(), nil, nil)
+	if err := checkPermission(&all_permitted_runtime, invoke_node_question_classifier_request); err != nil {
 		t.Errorf("checkPermission failed: %s", err.Error())
 	}
 
@@ -178,11 +153,16 @@ func TestBackwardsInvocationAllDeniedPermission(t *testing.T) {
 		t.Errorf("checkPermission failed: expected error, got nil")
 	}
 
-	invoke_node_request := NewBackwardsInvocation(dify_invocation.INVOKE_TYPE_NODE, "", getTestSession(), nil, nil)
+	invoke_node_request := NewBackwardsInvocation(dify_invocation.INVOKE_TYPE_NODE_PARAMETER_EXTRACTOR, "", getTestSession(), nil, nil)
 	if err := checkPermission(&all_denied_runtime, invoke_node_request); err == nil {
 		t.Errorf("checkPermission failed: expected error, got nil")
 	}
 
+	invoke_node_question_classifier_request := NewBackwardsInvocation(dify_invocation.INVOKE_TYPE_NODE_QUESTION_CLASSIFIER, "", getTestSession(), nil, nil)
+	if err := checkPermission(&all_denied_runtime, invoke_node_question_classifier_request); err == nil {
+		t.Errorf("checkPermission failed: expected error, got nil")
+	}
+
 	invoke_app_request := NewBackwardsInvocation(dify_invocation.INVOKE_TYPE_APP, "", getTestSession(), nil, nil)
 	if err := checkPermission(&all_denied_runtime, invoke_app_request); err == nil {
 		t.Errorf("checkPermission failed: expected error, got nil")