plugin.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. init_pkg "github.com/langgenius/dify-plugin-daemon/cmd/commandline/init"
  8. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_daemon/access_types"
  9. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_manager"
  10. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/decoder"
  11. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/packager"
  12. "github.com/langgenius/dify-plugin-daemon/internal/process"
  13. "github.com/langgenius/dify-plugin-daemon/internal/types/app"
  14. "github.com/langgenius/dify-plugin-daemon/internal/utils/log"
  15. "github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
  16. "github.com/langgenius/dify-plugin-daemon/internal/utils/routine"
  17. "github.com/spf13/cobra"
  18. )
  19. var (
  20. pluginInitCommand = &cobra.Command{
  21. Use: "init",
  22. Short: "Init",
  23. Long: "Init",
  24. Run: func(c *cobra.Command, args []string) {
  25. init_pkg.InitPlugin()
  26. },
  27. }
  28. pluginPackageCommand = &cobra.Command{
  29. Use: "package plugin_path [-o output_path]",
  30. Short: "Package",
  31. Long: "Package plugins",
  32. Run: func(cmd *cobra.Command, args []string) {
  33. if len(args) < 1 {
  34. fmt.Println("Error: plugin_path is required")
  35. return
  36. }
  37. input_path := args[0]
  38. // using filename of input_path as output_path if not specified
  39. output_path := ""
  40. if cmd.Flag("output_path") != nil {
  41. output_path = cmd.Flag("output_path").Value.String()
  42. } else {
  43. output_path = filepath.Base(input_path) + ".difypkg"
  44. }
  45. decoder, err := decoder.NewFSPluginDecoder(input_path)
  46. if err != nil {
  47. log.Error("failed to create plugin decoder , plugin path: %s, error: %v", input_path, err)
  48. return
  49. }
  50. packager := packager.NewPackager(decoder)
  51. zip_file, err := packager.Pack()
  52. if err != nil {
  53. log.Error("failed to package plugin %v", err)
  54. return
  55. }
  56. err = os.WriteFile(output_path, zip_file, 0644)
  57. if err != nil {
  58. log.Error("failed to write package file %v", err)
  59. return
  60. }
  61. log.Info("plugin packaged successfully, output path: %s", output_path)
  62. },
  63. }
  64. pluginChecksumCommand = &cobra.Command{
  65. Use: "checksum plugin_path",
  66. Short: "Checksum",
  67. Long: "Calculate the checksum of the plugin, you need specify the plugin path or .difypkg file path",
  68. Run: func(cmd *cobra.Command, args []string) {
  69. if len(args) < 1 {
  70. fmt.Println("Error: plugin_path is required")
  71. return
  72. }
  73. plugin_path := args[0]
  74. var plugin_decoder decoder.PluginDecoder
  75. if stat, err := os.Stat(plugin_path); err == nil {
  76. if stat.IsDir() {
  77. plugin_decoder, err = decoder.NewFSPluginDecoder(plugin_path)
  78. if err != nil {
  79. log.Error("failed to create plugin decoder, plugin path: %s, error: %v", plugin_path, err)
  80. return
  81. }
  82. } else {
  83. bytes, err := os.ReadFile(plugin_path)
  84. if err != nil {
  85. log.Error("failed to read plugin file, plugin path: %s, error: %v", plugin_path, err)
  86. return
  87. }
  88. plugin_decoder, err = decoder.NewZipPluginDecoder(bytes)
  89. if err != nil {
  90. log.Error("failed to create plugin decoder, plugin path: %s, error: %v", plugin_path, err)
  91. return
  92. }
  93. }
  94. } else {
  95. log.Error("failed to get plugin file info, plugin path: %s, error: %v", plugin_path, err)
  96. return
  97. }
  98. checksum, err := plugin_decoder.Checksum()
  99. if err != nil {
  100. log.Error("failed to calculate checksum, plugin path: %s, error: %v", plugin_path, err)
  101. return
  102. }
  103. log.Info("plugin checksum: %s", checksum)
  104. },
  105. }
  106. pluginPermissionCommand = &cobra.Command{
  107. Use: "permission",
  108. Short: "Permission",
  109. Long: `Permission, available values:
  110. tools - allow plugin to call tools
  111. models - allow plugin to call models
  112. models.llm - allow plugin to call llm
  113. models.text_embedding - allow plugin to call text_embedding model
  114. models.rerank - allow plugin to call rerank model
  115. models.tts - allow plugin to call tts
  116. models.speech2text - allow plugin to call speech2text
  117. models.moderation - allow plugin to call moderation
  118. apps - allow plugin to call apps
  119. storage - allow plugin to use storage
  120. endpoint - allow plugin to register endpoint`,
  121. }
  122. pluginPermissionAddCommand = &cobra.Command{
  123. Use: "add permission",
  124. Short: "",
  125. Long: "Add permission to plugin, you can find the available permission by running `dify plugin permission`",
  126. }
  127. pluginPermissionDropCommand = &cobra.Command{
  128. Use: "drop permission",
  129. Short: "",
  130. Long: "Drop permission from plugin, you can find the available permission by running `dify plugin permission`",
  131. }
  132. pluginTestCommand = &cobra.Command{
  133. Use: "test [-i inputs] [-t timeout] package_path invoke_type invoke_action",
  134. Short: "",
  135. 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" +
  136. "type: invoke type, available values: \n" +
  137. "[\n" +
  138. " tool, model, endpoint\n" +
  139. "]\n" +
  140. "action: invoke action, available values: \n" +
  141. "[\n" +
  142. " invoke_tool, validate_tool_credentials, \n" +
  143. " invoke_endpoint\n" +
  144. " invoke_llm, invoke_text_embedding, invoke_rerank, invoke_tts, invoke_speech2text, invoke_moderation, \n" +
  145. " validate_provider_credentials, validate_model_credentials, get_tts_model_voices, \n" +
  146. " get_text_embedding_num_tokens, get_ai_model_schemas, get_llm_num_tokens\n" +
  147. "]\n",
  148. Run: func(cmd *cobra.Command, args []string) {
  149. if len(args) < 3 {
  150. log.Error("invalid args, please specify package_path, invoke_type, invoke_action")
  151. return
  152. }
  153. // get package path
  154. package_path_str := args[0]
  155. // get invoke type
  156. invoke_type_str := args[1]
  157. // get invoke action
  158. invoke_action_str := args[2]
  159. // get inputs if specified
  160. inputs := map[string]any{}
  161. if cmd.Flag("inputs") != nil {
  162. inputs_str := cmd.Flag("inputs").Value.String()
  163. err := json.Unmarshal([]byte(inputs_str), &inputs)
  164. if err != nil {
  165. log.Error("failed to unmarshal inputs, inputs: %s, error: %v", inputs_str, err)
  166. return
  167. }
  168. }
  169. // parse flag
  170. timeout := ""
  171. if cmd.Flag("timeout") != nil {
  172. timeout = cmd.Flag("timeout").Value.String()
  173. }
  174. // get invoke_type and invoke_action
  175. invoke_type := access_types.PluginAccessType(invoke_type_str)
  176. if !invoke_type.IsValid() {
  177. log.Error("invalid invoke type: %s", invoke_type_str)
  178. return
  179. }
  180. invoke_action := access_types.PluginAccessAction(invoke_action_str)
  181. if !invoke_action.IsValid() {
  182. log.Error("invalid invoke action: %s", invoke_action_str)
  183. return
  184. }
  185. // init routine pool
  186. routine.InitPool(1024)
  187. // clean working directory when test finished
  188. defer os.RemoveAll("./working")
  189. // init testing config
  190. config := &app.Config{
  191. PluginWorkingPath: "./working/cwd",
  192. PluginStoragePath: "./working/storage",
  193. PluginMediaCachePath: "./working/media_cache",
  194. ProcessCachingPath: "./working/subprocesses",
  195. Platform: app.PLATFORM_LOCAL,
  196. }
  197. config.SetDefault()
  198. // init plugin manager
  199. plugin_manager := plugin_manager.InitGlobalManager(config)
  200. // start to schedule plugin subprocesses
  201. process.Init(config)
  202. // terminate all subprocesses when test finished
  203. defer process.TerminateAll()
  204. response, err := plugin_manager.TestPlugin(package_path_str, inputs, invoke_type, invoke_action, timeout)
  205. if err != nil {
  206. log.Error("failed to test plugin, package_path: %s, error: %v", package_path_str, err)
  207. return
  208. }
  209. for response.Next() {
  210. item, err := response.Read()
  211. if err != nil {
  212. log.Error("failed to read response item, error: %v", err)
  213. return
  214. }
  215. log.Info("%v", parser.MarshalJson(item))
  216. }
  217. },
  218. }
  219. )
  220. func init() {
  221. pluginCommand.AddCommand(pluginInitCommand)
  222. pluginCommand.AddCommand(pluginPackageCommand)
  223. pluginCommand.AddCommand(pluginChecksumCommand)
  224. pluginCommand.AddCommand(pluginPermissionCommand)
  225. pluginCommand.AddCommand(pluginTestCommand)
  226. pluginTestCommand.Flags().StringP("inputs", "i", "", "inputs")
  227. pluginTestCommand.Flags().StringP("timeout", "t", "", "timeout")
  228. pluginPermissionCommand.AddCommand(pluginPermissionAddCommand)
  229. pluginPermissionCommand.AddCommand(pluginPermissionDropCommand)
  230. }