plugin.go 7.9 KB

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