Explorar el Código

feat: support cli testing

Yeuoly hace 1 año
padre
commit
fefd3ef53e

+ 20 - 0
cmd/commandline/init/init.go

@@ -6,12 +6,17 @@ import (
 	"path/filepath"
 	"time"
 
+	_ "embed"
+
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/constants"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/log"
 )
 
+//go:embed templates/python/icon.svg
+var icon []byte
+
 func InitPlugin() {
 	m := initialize()
 	p := tea.NewProgram(m)
@@ -111,6 +116,7 @@ func (m model) createPlugin() {
 		PluginDeclarationWithoutAdvancedFields: plugin_entities.PluginDeclarationWithoutAdvancedFields{
 			Version:   "0.0.1",
 			Type:      plugin_entities.PluginType,
+			Icon:      "icon.svg",
 			Author:    m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Author(),
 			Name:      m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Name(),
 			CreatedAt: time.Now(),
@@ -172,6 +178,20 @@ func (m model) createPlugin() {
 		return
 	}
 
+	// create _assets directory
+	assets_dir := filepath.Join(plugin_dir, "_assets")
+	if err := os.MkdirAll(assets_dir, 0o755); err != nil {
+		log.Error("failed to create assets directory: %s", err)
+		return
+	}
+
+	// create icon.svg
+	icon_file_path := filepath.Join(assets_dir, "icon.svg")
+	if err := os.WriteFile(icon_file_path, icon, 0o644); err != nil {
+		log.Error("failed to write icon file: %s", err)
+		return
+	}
+
 	err = createPythonEnvironment(plugin_dir, manifest.Meta.Runner.Entrypoint)
 	if err != nil {
 		log.Error("failed to create python environment: %s", err)

+ 6 - 0
cmd/commandline/init/templates/python/icon.svg

@@ -0,0 +1,6 @@
+<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
+  <path d="M20 20 V80 M20 20 H60 Q80 20 80 40 T60 60 H20" 
+        fill="none" 
+        stroke="black" 
+        stroke-width="5"/>
+</svg>

+ 70 - 0
cmd/commandline/plugin.go

@@ -1,11 +1,13 @@
 package main
 
 import (
+	"encoding/json"
 	"fmt"
 	"os"
 	"path/filepath"
 
 	init_pkg "github.com/langgenius/dify-plugin-daemon/cmd/commandline/init"
+	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_daemon/access_types"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/decoder"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/packager"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/log"
@@ -140,6 +142,73 @@ endpoint				- allow plugin to register endpoint`,
 		Short: "",
 		Long:  "Drop permission from plugin, you can find the available permission by running `dify plugin permission`",
 	}
+
+	pluginTestCommand = &cobra.Command{
+		Use:   "test package_path invoke_type invoke_action [-i inputs]",
+		Short: "",
+		Long: "Test runs the given plugin package locally, and you can specify the inputs using json format, if not specified, will use default inputs\n" +
+			"type: invoke type, available values: \n" +
+			"[\n" +
+			"	tool, model, endpoint\n" +
+			"]\n" +
+			"action: invoke action, available values: \n" +
+			"[\n" +
+			"	invoke_tool, validate_tool_credentials, \n" +
+			"	invoke_endpoint\n" +
+			"	invoke_llm, invoke_text_embedding, invoke_rerank, invoke_tts, invoke_speech2text, invoke_moderation, \n" +
+			"	validate_provider_credentials, validate_model_credentials, get_tts_model_voices, \n" +
+			"	get_text_embedding_num_tokens, get_ai_model_schemas, get_llm_num_tokens\n" +
+			"]\n",
+		Run: func(cmd *cobra.Command, args []string) {
+			if len(args) < 3 {
+				log.Error("invalid args, please specify package_path, invoke_type, invoke_action")
+				return
+			}
+			// get package path
+			package_path_str := args[0]
+			// get invoke type
+			invoke_type_str := args[1]
+			// get invoke action
+			invoke_action_str := args[2]
+			// get inputs if specified
+			inputs_str := "{}"
+			if len(args) > 3 {
+				inputs_str = args[3]
+			}
+			inputs := map[string]any{}
+			err := json.Unmarshal([]byte(inputs_str), &inputs)
+			if err != nil {
+				log.Error("failed to unmarshal inputs, inputs: %s, error: %v", inputs_str, err)
+				return
+			}
+			// read package file
+			package_file, err := os.ReadFile(package_path_str)
+			if err != nil {
+				log.Error("failed to read package file, package path: %s, error: %v", package_path_str, err)
+				return
+			}
+			// create plugin decoder
+			plugin_decoder, err := decoder.NewZipPluginDecoder(package_file)
+			if err != nil {
+				log.Error("failed to create plugin decoder, package path: %s, error: %v", package_path_str, err)
+				return
+			}
+
+			// get invoke_type and invoke_action
+			invoke_type := access_types.PluginAccessType(invoke_type_str)
+			if !invoke_type.IsValid() {
+				log.Error("invalid invoke type: %s", invoke_type_str)
+				return
+			}
+			invoke_action := access_types.PluginAccessAction(invoke_action_str)
+			if !invoke_action.IsValid() {
+				log.Error("invalid invoke action: %s", invoke_action_str)
+				return
+			}
+
+			fmt.Println(plugin_decoder)
+		},
+	}
 )
 
 func init() {
@@ -147,6 +216,7 @@ func init() {
 	pluginCommand.AddCommand(pluginPackageCommand)
 	pluginCommand.AddCommand(pluginChecksumCommand)
 	pluginCommand.AddCommand(pluginPermissionCommand)
+	pluginCommand.AddCommand(pluginTestCommand)
 	pluginPermissionCommand.AddCommand(pluginPermissionAddCommand)
 	pluginPermissionCommand.AddCommand(pluginPermissionDropCommand)
 }

+ 1 - 24
cmd/tests/main.go

@@ -1,28 +1,5 @@
 package main
 
-import (
-	"encoding/json"
-	"fmt"
-
-	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
-)
-
-const data = `name: John
-age: 30
-a:
-  b: 2
-`
-
-type Test struct {
-	Name string `yaml:"name"`
-	Age  int    `yaml:"age"`
-	A    json.RawMessage
-}
-
 func main() {
-	ret, err := parser.UnmarshalYamlBytes[Test]([]byte(data))
-	if err != nil {
-		fmt.Println(err)
-	}
-	fmt.Println(ret)
+
 }

+ 2 - 0
go.mod

@@ -70,6 +70,8 @@ require (
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+	golang.org/x/tools v0.22.0 // indirect
+	golang.org/x/tools/go/pointer v0.1.0-deprecated // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 )
 

+ 4 - 0
go.sum

@@ -248,6 +248,10 @@ golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
 golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
 golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
+golang.org/x/tools/go/pointer v0.1.0-deprecated h1:PwCkqv2FT35Z4MVxR/tUlvLoL0TkxDjShpBrE4p18Ho=
+golang.org/x/tools/go/pointer v0.1.0-deprecated/go.mod h1:Jd+I2inNruJ+5VRdS+jU4S1t17z5y+UCCRa/eBRwilA=
 google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
 google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

+ 0 - 36
internal/core/dify_invocation/http_client.go

@@ -1,36 +0,0 @@
-package dify_invocation
-
-import (
-	"net"
-	"net/http"
-	"net/url"
-	"time"
-)
-
-var (
-	PLUGIN_INNER_API_KEY string
-	baseurl              *url.URL
-	client               *http.Client
-)
-
-func InitDifyInvocationDaemon(base string, calling_key string) error {
-	var err error
-	baseurl, err = url.Parse(base)
-	if err != nil {
-		return err
-	}
-
-	client = &http.Client{
-		Transport: &http.Transport{
-			Dial: (&net.Dialer{
-				Timeout:   5 * time.Second,
-				KeepAlive: 120 * time.Second,
-			}).Dial,
-			IdleConnTimeout: 120 * time.Second,
-		},
-	}
-
-	PLUGIN_INNER_API_KEY = calling_key
-
-	return nil
-}

+ 0 - 77
internal/core/dify_invocation/http_request.go

@@ -1,77 +0,0 @@
-package dify_invocation
-
-import (
-	"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/utils/http_requests"
-	"github.com/langgenius/dify-plugin-daemon/internal/utils/stream"
-)
-
-func Request[T any](method string, path string, options ...http_requests.HttpOptions) (*T, error) {
-	options = append(options,
-		http_requests.HttpHeader(map[string]string{
-			"X-Inner-Api-Key": PLUGIN_INNER_API_KEY,
-		}),
-		http_requests.HttpWriteTimeout(5000),
-		http_requests.HttpReadTimeout(60000),
-	)
-
-	return http_requests.RequestAndParse[T](client, difyPath(path), method, options...)
-}
-
-func StreamResponse[T any](method string, path string, options ...http_requests.HttpOptions) (*stream.Stream[T], error) {
-	options = append(
-		options, http_requests.HttpHeader(map[string]string{
-			"X-Inner-Api-Key": PLUGIN_INNER_API_KEY,
-		}),
-		http_requests.HttpWriteTimeout(5000),
-		http_requests.HttpReadTimeout(60000),
-	)
-
-	return http_requests.RequestAndParseStream[T](client, difyPath(path), method, options...)
-}
-
-func InvokeLLM(payload *InvokeLLMRequest) (*stream.Stream[model_entities.LLMResultChunk], error) {
-	return StreamResponse[model_entities.LLMResultChunk]("POST", "invoke/llm", http_requests.HttpPayloadJson(payload))
-}
-
-func InvokeTextEmbedding(payload *InvokeTextEmbeddingRequest) (*model_entities.TextEmbeddingResult, error) {
-	return Request[model_entities.TextEmbeddingResult]("POST", "invoke/text-embedding", http_requests.HttpPayloadJson(payload))
-}
-
-func InvokeRerank(payload *InvokeRerankRequest) (*model_entities.RerankResult, error) {
-	return Request[model_entities.RerankResult]("POST", "invoke/rerank", http_requests.HttpPayloadJson(payload))
-}
-
-func InvokeTTS(payload *InvokeTTSRequest) (*stream.Stream[model_entities.TTSResult], error) {
-	return StreamResponse[model_entities.TTSResult]("POST", "invoke/tts", http_requests.HttpPayloadJson(payload))
-}
-
-func InvokeSpeech2Text(payload *InvokeSpeech2TextRequest) (*model_entities.Speech2TextResult, error) {
-	return Request[model_entities.Speech2TextResult]("POST", "invoke/speech2text", http_requests.HttpPayloadJson(payload))
-}
-
-func InvokeModeration(payload *InvokeModerationRequest) (*model_entities.ModerationResult, error) {
-	return Request[model_entities.ModerationResult]("POST", "invoke/moderation", http_requests.HttpPayloadJson(payload))
-}
-
-func InvokeTool(payload *InvokeToolRequest) (*stream.Stream[tool_entities.ToolResponseChunk], error) {
-	return StreamResponse[tool_entities.ToolResponseChunk]("POST", "invoke/tool", http_requests.HttpPayloadJson(payload))
-}
-
-func InvokeApp(payload *InvokeAppRequest) (*stream.Stream[map[string]any], error) {
-	return StreamResponse[map[string]any]("POST", "invoke/app", http_requests.HttpPayloadJson(payload))
-}
-
-func InvokeEncrypt(payload *InvokeEncryptRequest) (map[string]any, error) {
-	if !payload.EncryptRequired(payload.Data) {
-		return payload.Data, nil
-	}
-
-	data, err := Request[map[string]any]("POST", "invoke/encrypt", http_requests.HttpPayloadJson(payload))
-	if err != nil {
-		return nil, err
-	}
-
-	return *data, nil
-}

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

@@ -0,0 +1,28 @@
+package dify_invocation
+
+import (
+	"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/utils/stream"
+)
+
+type BackwardsInvocation interface {
+	// InvokeLLM
+	InvokeLLM(payload *InvokeLLMRequest) (*stream.Stream[model_entities.LLMResultChunk], error)
+	// InvokeTextEmbedding
+	InvokeTextEmbedding(payload *InvokeTextEmbeddingRequest) (*model_entities.TextEmbeddingResult, error)
+	// InvokeRerank
+	InvokeRerank(payload *InvokeRerankRequest) (*model_entities.RerankResult, error)
+	// InvokeTTS
+	InvokeTTS(payload *InvokeTTSRequest) (*stream.Stream[model_entities.TTSResult], error)
+	// InvokeSpeech2Text
+	InvokeSpeech2Text(payload *InvokeSpeech2TextRequest) (*model_entities.Speech2TextResult, error)
+	// InvokeModeration
+	InvokeModeration(payload *InvokeModerationRequest) (*model_entities.ModerationResult, error)
+	// InvokeTool
+	InvokeTool(payload *InvokeToolRequest) (*stream.Stream[tool_entities.ToolResponseChunk], error)
+	// InvokeApp
+	InvokeApp(payload *InvokeAppRequest) (*stream.Stream[map[string]any], error)
+	// InvokeEncrypt
+	InvokeEncrypt(payload *InvokeEncryptRequest) (map[string]any, error)
+}

+ 0 - 6
internal/core/dify_invocation/path.go

@@ -1,6 +0,0 @@
-package dify_invocation
-
-func difyPath(path ...string) string {
-	path = append([]string{"inner", "api"}, path...)
-	return baseurl.JoinPath(path...).String()
-}

+ 18 - 15
internal/core/dify_invocation/encrypt_test.go

@@ -1,4 +1,4 @@
-package dify_invocation
+package real
 
 import (
 	"fmt"
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/gin-gonic/gin"
+	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/network"
 )
@@ -17,15 +18,15 @@ func TestEncryptRequired(t *testing.T) {
 		"key": "value",
 	}
 
-	payload := &InvokeEncryptRequest{
-		BaseInvokeDifyRequest: BaseInvokeDifyRequest{
+	payload := &dify_invocation.InvokeEncryptRequest{
+		BaseInvokeDifyRequest: dify_invocation.BaseInvokeDifyRequest{
 			TenantId: "123",
 			UserId:   "456",
-			Type:     INVOKE_TYPE_ENCRYPT,
+			Type:     dify_invocation.INVOKE_TYPE_ENCRYPT,
 		},
-		InvokeEncryptSchema: InvokeEncryptSchema{
-			Opt:       ENCRYPT_OPT_ENCRYPT,
-			Namespace: ENCRYPT_NAMESPACE_ENDPOINT,
+		InvokeEncryptSchema: dify_invocation.InvokeEncryptSchema{
+			Opt:       dify_invocation.ENCRYPT_OPT_ENCRYPT,
+			Namespace: dify_invocation.ENCRYPT_NAMESPACE_ENDPOINT,
 			Identity:  "test123",
 			Data:      data,
 			Config: map[string]plugin_entities.ProviderConfig{
@@ -94,19 +95,21 @@ func TestInvokeEncrypt(t *testing.T) {
 
 	time.Sleep(1 * time.Second)
 
-	if err := InitDifyInvocationDaemon(fmt.Sprintf("http://localhost:%d", port), "test"); err != nil {
+	i, err := InitDifyInvocationDaemon(fmt.Sprintf("http://localhost:%d", port), "test")
+	if err != nil {
 		t.Errorf("InitDifyInvocationDaemon failed: %v", err)
+		return
 	}
 
-	payload := &InvokeEncryptRequest{
-		BaseInvokeDifyRequest: BaseInvokeDifyRequest{
+	payload := &dify_invocation.InvokeEncryptRequest{
+		BaseInvokeDifyRequest: dify_invocation.BaseInvokeDifyRequest{
 			TenantId: "123",
 			UserId:   "456",
-			Type:     INVOKE_TYPE_ENCRYPT,
+			Type:     dify_invocation.INVOKE_TYPE_ENCRYPT,
 		},
-		InvokeEncryptSchema: InvokeEncryptSchema{
-			Opt:       ENCRYPT_OPT_ENCRYPT,
-			Namespace: ENCRYPT_NAMESPACE_ENDPOINT,
+		InvokeEncryptSchema: dify_invocation.InvokeEncryptSchema{
+			Opt:       dify_invocation.ENCRYPT_OPT_ENCRYPT,
+			Namespace: dify_invocation.ENCRYPT_NAMESPACE_ENDPOINT,
 			Identity:  "test123",
 			Data:      map[string]any{"key": "value"},
 			Config: map[string]plugin_entities.ProviderConfig{
@@ -118,7 +121,7 @@ func TestInvokeEncrypt(t *testing.T) {
 		},
 	}
 
-	if encrypted, err := InvokeEncrypt(payload); err != nil {
+	if encrypted, err := i.InvokeEncrypt(payload); err != nil {
 		t.Errorf("InvokeEncrypt failed: %v", err)
 	} else {
 		if encrypted["key"] != "encrypted" {

+ 35 - 0
internal/core/dify_invocation/real/http_client.go

@@ -0,0 +1,35 @@
+package real
+
+import (
+	"net"
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation"
+)
+
+func InitDifyInvocationDaemon(base string, calling_key string) (dify_invocation.BackwardsInvocation, error) {
+	var err error
+	invocation := &RealBackwardsInvocation{}
+	baseurl, err := url.Parse(base)
+	if err != nil {
+		return nil, err
+	}
+
+	client := &http.Client{
+		Transport: &http.Transport{
+			Dial: (&net.Dialer{
+				Timeout:   5 * time.Second,
+				KeepAlive: 120 * time.Second,
+			}).Dial,
+			IdleConnTimeout: 120 * time.Second,
+		},
+	}
+
+	invocation.baseurl = baseurl
+	invocation.client = client
+	invocation.PLUGIN_INNER_API_KEY = calling_key
+
+	return invocation, nil
+}

+ 78 - 0
internal/core/dify_invocation/real/http_request.go

@@ -0,0 +1,78 @@
+package real
+
+import (
+	"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/utils/http_requests"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/stream"
+)
+
+func Request[T any](i *RealBackwardsInvocation, method string, path string, options ...http_requests.HttpOptions) (*T, error) {
+	options = append(options,
+		http_requests.HttpHeader(map[string]string{
+			"X-Inner-Api-Key": i.PLUGIN_INNER_API_KEY,
+		}),
+		http_requests.HttpWriteTimeout(5000),
+		http_requests.HttpReadTimeout(240000),
+	)
+
+	return http_requests.RequestAndParse[T](i.client, i.difyPath(path), method, options...)
+}
+
+func StreamResponse[T any](i *RealBackwardsInvocation, method string, path string, options ...http_requests.HttpOptions) (*stream.Stream[T], error) {
+	options = append(
+		options, http_requests.HttpHeader(map[string]string{
+			"X-Inner-Api-Key": i.PLUGIN_INNER_API_KEY,
+		}),
+		http_requests.HttpWriteTimeout(5000),
+		http_requests.HttpReadTimeout(240000),
+	)
+
+	return http_requests.RequestAndParseStream[T](i.client, i.difyPath(path), method, options...)
+}
+
+func (i *RealBackwardsInvocation) InvokeLLM(payload *dify_invocation.InvokeLLMRequest) (*stream.Stream[model_entities.LLMResultChunk], error) {
+	return StreamResponse[model_entities.LLMResultChunk](i, "POST", "invoke/llm", http_requests.HttpPayloadJson(payload))
+}
+
+func (i *RealBackwardsInvocation) InvokeTextEmbedding(payload *dify_invocation.InvokeTextEmbeddingRequest) (*model_entities.TextEmbeddingResult, error) {
+	return Request[model_entities.TextEmbeddingResult](i, "POST", "invoke/text-embedding", http_requests.HttpPayloadJson(payload))
+}
+
+func (i *RealBackwardsInvocation) InvokeRerank(payload *dify_invocation.InvokeRerankRequest) (*model_entities.RerankResult, error) {
+	return Request[model_entities.RerankResult](i, "POST", "invoke/rerank", http_requests.HttpPayloadJson(payload))
+}
+
+func (i *RealBackwardsInvocation) InvokeTTS(payload *dify_invocation.InvokeTTSRequest) (*stream.Stream[model_entities.TTSResult], error) {
+	return StreamResponse[model_entities.TTSResult](i, "POST", "invoke/tts", http_requests.HttpPayloadJson(payload))
+}
+
+func (i *RealBackwardsInvocation) InvokeSpeech2Text(payload *dify_invocation.InvokeSpeech2TextRequest) (*model_entities.Speech2TextResult, error) {
+	return Request[model_entities.Speech2TextResult](i, "POST", "invoke/speech2text", http_requests.HttpPayloadJson(payload))
+}
+
+func (i *RealBackwardsInvocation) InvokeModeration(payload *dify_invocation.InvokeModerationRequest) (*model_entities.ModerationResult, error) {
+	return Request[model_entities.ModerationResult](i, "POST", "invoke/moderation", http_requests.HttpPayloadJson(payload))
+}
+
+func (i *RealBackwardsInvocation) InvokeTool(payload *dify_invocation.InvokeToolRequest) (*stream.Stream[tool_entities.ToolResponseChunk], error) {
+	return StreamResponse[tool_entities.ToolResponseChunk](i, "POST", "invoke/tool", http_requests.HttpPayloadJson(payload))
+}
+
+func (i *RealBackwardsInvocation) InvokeApp(payload *dify_invocation.InvokeAppRequest) (*stream.Stream[map[string]any], error) {
+	return StreamResponse[map[string]any](i, "POST", "invoke/app", 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
+	}
+
+	data, err := Request[map[string]any](i, "POST", "invoke/encrypt", http_requests.HttpPayloadJson(payload))
+	if err != nil {
+		return nil, err
+	}
+
+	return *data, nil
+}

+ 6 - 0
internal/core/dify_invocation/real/path.go

@@ -0,0 +1,6 @@
+package real
+
+func (r *RealBackwardsInvocation) difyPath(path ...string) string {
+	path = append([]string{"inner", "api"}, path...)
+	return r.baseurl.JoinPath(path...).String()
+}

+ 12 - 0
internal/core/dify_invocation/real/types.go

@@ -0,0 +1,12 @@
+package real
+
+import (
+	"net/http"
+	"net/url"
+)
+
+type RealBackwardsInvocation struct {
+	PLUGIN_INNER_API_KEY string
+	baseurl              *url.URL
+	client               *http.Client
+}

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

@@ -0,0 +1,191 @@
+package tester
+
+import (
+	"time"
+
+	"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/utils/stream"
+)
+
+type MockedDifyInvocation struct{}
+
+func NewMockedDifyInvocation() dify_invocation.BackwardsInvocation {
+	return &MockedDifyInvocation{}
+}
+
+func (m *MockedDifyInvocation) InvokeLLM(payload *dify_invocation.InvokeLLMRequest) (*stream.Stream[model_entities.LLMResultChunk], error) {
+	stream := stream.NewStream[model_entities.LLMResultChunk](5)
+	go func() {
+		stream.Write(model_entities.LLMResultChunk{
+			Model:             model_entities.LLMModel(payload.Model),
+			PromptMessages:    payload.PromptMessages,
+			SystemFingerprint: "test",
+			Delta: model_entities.LLMResultChunkDelta{
+				Index: &[]int{1}[0],
+				Message: model_entities.PromptMessage{
+					Role:    model_entities.PROMPT_MESSAGE_ROLE_ASSISTANT,
+					Content: "hello",
+					Name:    "test",
+				},
+			},
+		})
+		time.Sleep(100 * time.Millisecond)
+		stream.Write(model_entities.LLMResultChunk{
+			Model:             model_entities.LLMModel(payload.Model),
+			PromptMessages:    payload.PromptMessages,
+			SystemFingerprint: "test",
+			Delta: model_entities.LLMResultChunkDelta{
+				Index: &[]int{1}[0],
+				Message: model_entities.PromptMessage{
+					Role:    model_entities.PROMPT_MESSAGE_ROLE_ASSISTANT,
+					Content: " world",
+					Name:    "test",
+				},
+			},
+		})
+		time.Sleep(100 * time.Millisecond)
+		stream.Write(model_entities.LLMResultChunk{
+			Model:             model_entities.LLMModel(payload.Model),
+			PromptMessages:    payload.PromptMessages,
+			SystemFingerprint: "test",
+			Delta: model_entities.LLMResultChunkDelta{
+				Index: &[]int{2}[0],
+				Message: model_entities.PromptMessage{
+					Role:    model_entities.PROMPT_MESSAGE_ROLE_ASSISTANT,
+					Content: " world",
+					Name:    "test",
+				},
+			},
+		})
+		time.Sleep(100 * time.Millisecond)
+		stream.Write(model_entities.LLMResultChunk{
+			Model:             model_entities.LLMModel(payload.Model),
+			PromptMessages:    payload.PromptMessages,
+			SystemFingerprint: "test",
+			Delta: model_entities.LLMResultChunkDelta{
+				Index: &[]int{3}[0],
+				Message: model_entities.PromptMessage{
+					Role:    model_entities.PROMPT_MESSAGE_ROLE_ASSISTANT,
+					Content: " !",
+					Name:    "test",
+				},
+			},
+		})
+		time.Sleep(100 * time.Millisecond)
+		stream.Write(model_entities.LLMResultChunk{
+			Model:             model_entities.LLMModel(payload.Model),
+			PromptMessages:    payload.PromptMessages,
+			SystemFingerprint: "test",
+			Delta: model_entities.LLMResultChunkDelta{
+				Index: &[]int{3}[0],
+				Usage: &model_entities.LLMUsage{
+					PromptTokens:     &[]int{100}[0],
+					CompletionTokens: &[]int{100}[0],
+					TotalTokens:      &[]int{200}[0],
+					Latency:          &[]float64{0.4}[0],
+					Currency:         &[]string{"USD"}[0],
+				},
+			},
+		})
+		stream.Close()
+	}()
+	return stream, nil
+}
+
+func (m *MockedDifyInvocation) InvokeTextEmbedding(payload *dify_invocation.InvokeTextEmbeddingRequest) (*model_entities.TextEmbeddingResult, error) {
+	result := model_entities.TextEmbeddingResult{
+		Model: payload.Model,
+		Usage: model_entities.EmbeddingUsage{
+			Tokens:      &[]int{100}[0],
+			TotalTokens: &[]int{100}[0],
+			Latency:     &[]float64{0.1}[0],
+			Currency:    &[]string{"USD"}[0],
+		},
+	}
+
+	for range payload.Texts {
+		result.Embeddings = append(result.Embeddings, []float64{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0})
+	}
+
+	return &result, nil
+}
+
+func (m *MockedDifyInvocation) InvokeRerank(payload *dify_invocation.InvokeRerankRequest) (*model_entities.RerankResult, error) {
+	result := model_entities.RerankResult{
+		Model: payload.Model,
+	}
+	for i, doc := range payload.Docs {
+		result.Docs = append(result.Docs, model_entities.RerankDocument{
+			Index: &[]int{i}[0],
+			Score: &[]float64{0.1}[0],
+			Text:  &doc,
+		})
+	}
+	return &result, nil
+}
+
+func (m *MockedDifyInvocation) InvokeTTS(payload *dify_invocation.InvokeTTSRequest) (*stream.Stream[model_entities.TTSResult], error) {
+	stream := stream.NewStream[model_entities.TTSResult](5)
+	go func() {
+		for i := 0; i < 10; i++ {
+			stream.Write(model_entities.TTSResult{
+				Result: "a1b2c3d4",
+			})
+			time.Sleep(100 * time.Millisecond)
+		}
+		stream.Close()
+	}()
+	return stream, nil
+}
+
+func (m *MockedDifyInvocation) InvokeSpeech2Text(payload *dify_invocation.InvokeSpeech2TextRequest) (*model_entities.Speech2TextResult, error) {
+	result := model_entities.Speech2TextResult{
+		Result: "hello world",
+	}
+	return &result, nil
+}
+
+func (m *MockedDifyInvocation) InvokeModeration(payload *dify_invocation.InvokeModerationRequest) (*model_entities.ModerationResult, error) {
+	result := model_entities.ModerationResult{
+		Result: true,
+	}
+	return &result, nil
+}
+
+func (m *MockedDifyInvocation) InvokeTool(payload *dify_invocation.InvokeToolRequest) (*stream.Stream[tool_entities.ToolResponseChunk], error) {
+	stream := stream.NewStream[tool_entities.ToolResponseChunk](5)
+	go func() {
+		for i := 0; i < 10; i++ {
+			stream.Write(tool_entities.ToolResponseChunk{
+				Type: tool_entities.ToolResponseChunkTypeText,
+				Message: map[string]any{
+					"text": "hello world",
+				},
+			})
+			time.Sleep(100 * time.Millisecond)
+		}
+		stream.Close()
+	}()
+
+	return stream, nil
+}
+
+func (m *MockedDifyInvocation) InvokeApp(payload *dify_invocation.InvokeAppRequest) (*stream.Stream[map[string]any], error) {
+	stream := stream.NewStream[map[string]any](5)
+	go func() {
+		for i := 0; i < 10; i++ {
+			stream.Write(map[string]any{
+				"text": "hello world",
+			})
+			time.Sleep(100 * time.Millisecond)
+		}
+		stream.Close()
+	}()
+	return stream, nil
+}
+
+func (m *MockedDifyInvocation) InvokeEncrypt(payload *dify_invocation.InvokeEncryptRequest) (map[string]any, error) {
+	return payload.Data, nil
+}

+ 24 - 0
internal/core/plugin_daemon/access_types/access.go

@@ -8,6 +8,12 @@ const (
 	PLUGIN_ACCESS_TYPE_ENDPOINT PluginAccessType = "endpoint"
 )
 
+func (p PluginAccessType) IsValid() bool {
+	return p == PLUGIN_ACCESS_TYPE_TOOL ||
+		p == PLUGIN_ACCESS_TYPE_MODEL ||
+		p == PLUGIN_ACCESS_TYPE_ENDPOINT
+}
+
 type PluginAccessAction string
 
 const (
@@ -27,3 +33,21 @@ const (
 	PLUGIN_ACCESS_ACTION_GET_AI_MODEL_SCHEMAS          PluginAccessAction = "get_ai_model_schemas"
 	PLUGIN_ACCESS_ACTION_GET_LLM_NUM_TOKENS            PluginAccessAction = "get_llm_num_tokens"
 )
+
+func (p PluginAccessAction) IsValid() bool {
+	return p == PLUGIN_ACCESS_ACTION_INVOKE_TOOL ||
+		p == PLUGIN_ACCESS_ACTION_VALIDATE_TOOL_CREDENTIALS ||
+		p == PLUGIN_ACCESS_ACTION_INVOKE_LLM ||
+		p == PLUGIN_ACCESS_ACTION_INVOKE_TEXT_EMBEDDING ||
+		p == PLUGIN_ACCESS_ACTION_INVOKE_RERANK ||
+		p == PLUGIN_ACCESS_ACTION_INVOKE_TTS ||
+		p == PLUGIN_ACCESS_ACTION_INVOKE_SPEECH2TEXT ||
+		p == PLUGIN_ACCESS_ACTION_INVOKE_MODERATION ||
+		p == PLUGIN_ACCESS_ACTION_VALIDATE_PROVIDER_CREDENTIALS ||
+		p == PLUGIN_ACCESS_ACTION_VALIDATE_MODEL_CREDENTIALS ||
+		p == PLUGIN_ACCESS_ACTION_INVOKE_ENDPOINT ||
+		p == PLUGIN_ACCESS_ACTION_GET_TTS_MODEL_VOICES ||
+		p == PLUGIN_ACCESS_ACTION_GET_TEXT_EMBEDDING_NUM_TOKENS ||
+		p == PLUGIN_ACCESS_ACTION_GET_AI_MODEL_SCHEMAS ||
+		p == PLUGIN_ACCESS_ACTION_GET_LLM_NUM_TOKENS
+}

+ 9 - 5
internal/core/plugin_daemon/backwards_invocation/request.go

@@ -30,6 +30,9 @@ type BackwardsInvocation struct {
 	// writer is the writer that writes the data to the session
 	// NOTE: write operation will not raise errors
 	writer BackwardsInvocationWriter
+
+	// backwardsInvocation is the backwards invocation that is used to invoke dify
+	backwardsInvocation dify_invocation.BackwardsInvocation
 }
 
 func NewBackwardsInvocation(
@@ -40,11 +43,12 @@ func NewBackwardsInvocation(
 	detailed_request map[string]any,
 ) *BackwardsInvocation {
 	return &BackwardsInvocation{
-		typ:              typ,
-		id:               id,
-		detailed_request: detailed_request,
-		session:          session,
-		writer:           writer,
+		typ:                 typ,
+		id:                  id,
+		detailed_request:    detailed_request,
+		session:             session,
+		writer:              writer,
+		backwardsInvocation: session.BackwardsInvocation(),
 	}
 }
 

+ 13 - 9
internal/core/plugin_daemon/backwards_invocation/task.go

@@ -34,7 +34,11 @@ func InvokeDify(
 	}
 
 	// prepare invocation arguments
-	request_handle, err := prepareDifyInvocationArguments(session, writer, request)
+	request_handle, err := prepareDifyInvocationArguments(
+		session,
+		writer,
+		request,
+	)
 	if err != nil {
 		return err
 	}
@@ -253,7 +257,7 @@ func executeDifyInvocationToolTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeToolRequest,
 ) {
-	response, err := dify_invocation.InvokeTool(request)
+	response, err := handle.backwardsInvocation.InvokeTool(request)
 	if err != nil {
 		handle.WriteError(fmt.Errorf("invoke tool failed: %s", err.Error()))
 		return
@@ -268,7 +272,7 @@ func executeDifyInvocationLLMTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeLLMRequest,
 ) {
-	response, err := dify_invocation.InvokeLLM(request)
+	response, err := handle.backwardsInvocation.InvokeLLM(request)
 	if err != nil {
 		handle.WriteError(fmt.Errorf("invoke llm model failed: %s", err.Error()))
 		return
@@ -283,7 +287,7 @@ func executeDifyInvocationTextEmbeddingTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeTextEmbeddingRequest,
 ) {
-	response, err := dify_invocation.InvokeTextEmbedding(request)
+	response, err := handle.backwardsInvocation.InvokeTextEmbedding(request)
 	if err != nil {
 		handle.WriteError(fmt.Errorf("invoke text-embedding model failed: %s", err.Error()))
 		return
@@ -296,7 +300,7 @@ func executeDifyInvocationRerankTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeRerankRequest,
 ) {
-	response, err := dify_invocation.InvokeRerank(request)
+	response, err := handle.backwardsInvocation.InvokeRerank(request)
 	if err != nil {
 		handle.WriteError(fmt.Errorf("invoke rerank model failed: %s", err.Error()))
 		return
@@ -309,7 +313,7 @@ func executeDifyInvocationTTSTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeTTSRequest,
 ) {
-	response, err := dify_invocation.InvokeTTS(request)
+	response, err := handle.backwardsInvocation.InvokeTTS(request)
 	if err != nil {
 		handle.WriteError(fmt.Errorf("invoke tts model failed: %s", err.Error()))
 		return
@@ -324,7 +328,7 @@ func executeDifyInvocationSpeech2TextTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeSpeech2TextRequest,
 ) {
-	response, err := dify_invocation.InvokeSpeech2Text(request)
+	response, err := handle.backwardsInvocation.InvokeSpeech2Text(request)
 	if err != nil {
 		handle.WriteError(fmt.Errorf("invoke speech2text model failed: %s", err.Error()))
 		return
@@ -337,7 +341,7 @@ func executeDifyInvocationModerationTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeModerationRequest,
 ) {
-	response, err := dify_invocation.InvokeModeration(request)
+	response, err := handle.backwardsInvocation.InvokeModeration(request)
 	if err != nil {
 		handle.WriteError(fmt.Errorf("invoke moderation model failed: %s", err.Error()))
 		return
@@ -350,7 +354,7 @@ func executeDifyInvocationAppTask(
 	handle *BackwardsInvocation,
 	request *dify_invocation.InvokeAppRequest,
 ) {
-	response, err := dify_invocation.InvokeApp(request)
+	response, err := handle.backwardsInvocation.InvokeApp(request)
 	if err != nil {
 		handle.WriteError(fmt.Errorf("invoke app failed: %s", err.Error()))
 		return

+ 1 - 0
internal/core/plugin_manager/local_manager/tester.go

@@ -0,0 +1 @@
+package local_manager

+ 12 - 2
internal/core/plugin_manager/manager.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation"
+	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation/real"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_manager/media_manager"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_manager/serverless"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/decoder"
@@ -36,6 +37,9 @@ type PluginManager struct {
 
 	// Install is a function that installs a plugin to the platform
 	Install func(tenant_id string, decoder decoder.PluginDecoder) (*stream.Stream[PluginInstallResponse], error)
+
+	// backwardsInvocation is a handle to invoke dify
+	backwardsInvocation dify_invocation.BackwardsInvocation
 }
 
 var (
@@ -111,11 +115,13 @@ func (p *PluginManager) Init(configuration *app.Config) {
 		log.Panic("init redis client failed: %s", err.Error())
 	}
 
-	if err := dify_invocation.InitDifyInvocationDaemon(
+	invocation, err := real.InitDifyInvocationDaemon(
 		configuration.PluginInnerApiURL, configuration.PluginInnerApiKey,
-	); err != nil {
+	)
+	if err != nil {
 		log.Panic("init dify invocation daemon failed: %s", err.Error())
 	}
+	p.backwardsInvocation = invocation
 
 	// start local watcher
 	if configuration.Platform == app.PLATFORM_LOCAL {
@@ -125,3 +131,7 @@ func (p *PluginManager) Init(configuration *app.Config) {
 	// start remote watcher
 	p.startRemoteWatcher(configuration)
 }
+
+func (p *PluginManager) BackwardsInvocation() dify_invocation.BackwardsInvocation {
+	return p.backwardsInvocation
+}

+ 10 - 4
internal/core/session_manager/session.go

@@ -7,7 +7,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
-	"github.com/langgenius/dify-plugin-daemon/internal/core/persistence"
+	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_daemon/access_types"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/cache"
@@ -22,9 +22,9 @@ var (
 
 // session need to implement the backwards_invocation.BackwardsInvocationWriter interface
 type Session struct {
-	ID          string                         `json:"id"`
-	runtime     plugin_entities.PluginLifetime `json:"-"`
-	persistence *persistence.Persistence       `json:"-"`
+	ID                  string                              `json:"id"`
+	runtime             plugin_entities.PluginLifetime      `json:"-"`
+	backwardsInvocation dify_invocation.BackwardsInvocation `json:"-"`
 
 	TenantID               string                                 `json:"tenant_id"`
 	UserID                 string                                 `json:"user_id"`
@@ -47,6 +47,7 @@ func NewSession(
 	invoke_from access_types.PluginAccessType,
 	action access_types.PluginAccessAction,
 	declaration *plugin_entities.PluginDeclaration,
+	backwardsInvocation dify_invocation.BackwardsInvocation,
 ) *Session {
 	s := &Session{
 		ID:                     uuid.New().String(),
@@ -57,6 +58,7 @@ func NewSession(
 		InvokeFrom:             invoke_from,
 		Action:                 action,
 		Declaration:            declaration,
+		backwardsInvocation:    backwardsInvocation,
 	}
 
 	session_lock.Lock()
@@ -110,6 +112,10 @@ func (s *Session) Runtime() plugin_entities.PluginLifetime {
 	return s.runtime
 }
 
+func (s *Session) BackwardsInvocation() dify_invocation.BackwardsInvocation {
+	return s.backwardsInvocation
+}
+
 type PLUGIN_IN_STREAM_EVENT string
 
 const (

+ 14 - 1
internal/server/controllers/base.go

@@ -10,9 +10,22 @@ import (
 
 func BindRequest[T any](r *gin.Context, success func(T)) {
 	var request T
+	var err error
 
-	err := r.ShouldBindUri(&request)
+	if r.Request.Header.Get("Content-Type") == "application/json" {
+		err = r.ShouldBindJSON(&request)
+	} else {
+		err = r.ShouldBind(&request)
+	}
+
+	if err != nil {
+		resp := entities.NewErrorResponse(-400, err.Error())
+		r.JSON(400, resp)
+		return
+	}
 
+	// bind uri
+	err = r.ShouldBindUri(&request)
 	if err != nil {
 		resp := entities.NewErrorResponse(-400, err.Error())
 		r.JSON(400, resp)

+ 8 - 2
internal/service/endpoint.go

@@ -61,7 +61,7 @@ func Endpoint(
 	}
 
 	// decrypt settings
-	settings, err := dify_invocation.InvokeEncrypt(&dify_invocation.InvokeEncryptRequest{
+	settings, err := manager.BackwardsInvocation().InvokeEncrypt(&dify_invocation.InvokeEncryptRequest{
 		BaseInvokeDifyRequest: dify_invocation.BaseInvokeDifyRequest{
 			TenantId: endpoint.TenantID,
 			UserId:   "",
@@ -89,6 +89,7 @@ func Endpoint(
 		access_types.PLUGIN_ACCESS_TYPE_ENDPOINT,
 		access_types.PLUGIN_ACCESS_ACTION_INVOKE_ENDPOINT,
 		runtime.Configuration(),
+		manager.BackwardsInvocation(),
 	)
 	defer session.Close()
 
@@ -190,6 +191,11 @@ func ListEndpoints(tenant_id string, page int, page_size int) *entities.Response
 		return entities.NewErrorResponse(-500, fmt.Sprintf("failed to list endpoints: %v", err))
 	}
 
+	manager := plugin_manager.Manager()
+	if manager == nil {
+		return entities.NewErrorResponse(-500, "failed to get plugin manager")
+	}
+
 	// decrypt settings
 	for i, endpoint := range endpoints {
 		plugin_installation, err := db.GetOne[models.PluginInstallation](
@@ -216,7 +222,7 @@ func ListEndpoints(tenant_id string, page int, page_size int) *entities.Response
 			return entities.NewErrorResponse(-404, "plugin does not have an endpoint")
 		}
 
-		decrypted_settings, err := dify_invocation.InvokeEncrypt(&dify_invocation.InvokeEncryptRequest{
+		decrypted_settings, err := manager.BackwardsInvocation().InvokeEncrypt(&dify_invocation.InvokeEncryptRequest{
 			BaseInvokeDifyRequest: dify_invocation.BaseInvokeDifyRequest{
 				TenantId: tenant_id,
 				UserId:   "",

+ 14 - 1
internal/service/invoke_tool.go

@@ -1,6 +1,8 @@
 package service
 
 import (
+	"errors"
+
 	"github.com/gin-gonic/gin"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_daemon"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_daemon/access_types"
@@ -18,7 +20,17 @@ func createSession[T any](
 	access_action access_types.PluginAccessAction,
 	cluster_id string,
 ) (*session_manager.Session, error) {
-	runtime := plugin_manager.Manager().Get(r.PluginUniqueIdentifier)
+	manager := plugin_manager.Manager()
+	if manager == nil {
+		return nil, errors.New("failed to get plugin manager")
+	}
+	runtime := manager.Get(r.PluginUniqueIdentifier)
+	if runtime == nil {
+		return nil, errors.New("failed to get plugin runtime")
+	}
+	if runtime == nil {
+		return nil, errors.New("failed to get plugin runtime")
+	}
 
 	session := session_manager.NewSession(
 		r.TenantId,
@@ -28,6 +40,7 @@ func createSession[T any](
 		access_type,
 		access_action,
 		runtime.Configuration(),
+		manager.BackwardsInvocation(),
 	)
 
 	session.BindRuntime(runtime)

+ 7 - 1
internal/service/setup_endpoint.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/langgenius/dify-plugin-daemon/internal/core/dify_invocation"
+	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_manager"
 	"github.com/langgenius/dify-plugin-daemon/internal/db"
 	"github.com/langgenius/dify-plugin-daemon/internal/service/install_service"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities"
@@ -47,8 +48,13 @@ func SetupEndpoint(
 		return entities.NewErrorResponse(-404, "plugin does not have an endpoint")
 	}
 
+	manager := plugin_manager.Manager()
+	if manager == nil {
+		return entities.NewErrorResponse(-500, "failed to get plugin manager")
+	}
+
 	// encrypt settings
-	encrypted_settings, err := dify_invocation.InvokeEncrypt(
+	encrypted_settings, err := manager.BackwardsInvocation().InvokeEncrypt(
 		&dify_invocation.InvokeEncryptRequest{
 			BaseInvokeDifyRequest: dify_invocation.BaseInvokeDifyRequest{
 				TenantId: tenant_id,