init.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. package plugin
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "time"
  7. _ "embed"
  8. tea "github.com/charmbracelet/bubbletea"
  9. "github.com/langgenius/dify-plugin-daemon/internal/types/entities/constants"
  10. "github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
  11. "github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
  12. "github.com/langgenius/dify-plugin-daemon/internal/utils/log"
  13. )
  14. //go:embed templates/python/icon.svg
  15. var icon []byte
  16. func InitPlugin() {
  17. m := initialize()
  18. p := tea.NewProgram(m)
  19. if result, err := p.Run(); err != nil {
  20. fmt.Println("Error running program:", err)
  21. } else {
  22. if m, ok := result.(model); ok {
  23. if m.completed {
  24. m.createPlugin()
  25. }
  26. } else {
  27. log.Error("Error running program:", err)
  28. return
  29. }
  30. }
  31. }
  32. type subMenuKey string
  33. const (
  34. SUB_MENU_KEY_PROFILE subMenuKey = "profile"
  35. SUB_MENU_KEY_LANGUAGE subMenuKey = "language"
  36. SUB_MENU_KEY_CATEGORY subMenuKey = "category"
  37. SUB_MENU_KEY_PERMISSION subMenuKey = "permission"
  38. )
  39. type model struct {
  40. subMenus map[subMenuKey]subMenu
  41. subMenuSeq []subMenuKey
  42. currentSubMenu subMenuKey
  43. completed bool
  44. }
  45. func initialize() model {
  46. m := model{}
  47. m.subMenus = map[subMenuKey]subMenu{
  48. SUB_MENU_KEY_PROFILE: newProfile(),
  49. SUB_MENU_KEY_LANGUAGE: newLanguage(),
  50. SUB_MENU_KEY_CATEGORY: newCategory(),
  51. SUB_MENU_KEY_PERMISSION: newPermission(),
  52. }
  53. m.currentSubMenu = SUB_MENU_KEY_PROFILE
  54. m.subMenuSeq = []subMenuKey{
  55. SUB_MENU_KEY_PROFILE,
  56. SUB_MENU_KEY_LANGUAGE,
  57. SUB_MENU_KEY_CATEGORY,
  58. SUB_MENU_KEY_PERMISSION,
  59. }
  60. return m
  61. }
  62. func (m model) Init() tea.Cmd {
  63. return nil
  64. }
  65. func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  66. currentSubMenu, event, cmd := m.subMenus[m.currentSubMenu].Update(msg)
  67. m.subMenus[m.currentSubMenu] = currentSubMenu
  68. switch event {
  69. case SUB_MENU_EVENT_NEXT:
  70. if m.currentSubMenu != m.subMenuSeq[len(m.subMenuSeq)-1] {
  71. // move the current sub menu to the next one
  72. for i, key := range m.subMenuSeq {
  73. if key == m.currentSubMenu {
  74. m.currentSubMenu = m.subMenuSeq[i+1]
  75. break
  76. }
  77. }
  78. } else {
  79. m.completed = true
  80. return m, tea.Quit
  81. }
  82. case SUB_MENU_EVENT_PREV:
  83. if m.currentSubMenu != m.subMenuSeq[0] {
  84. // move the current sub menu to the previous one
  85. for i, key := range m.subMenuSeq {
  86. if key == m.currentSubMenu {
  87. m.currentSubMenu = m.subMenuSeq[i-1]
  88. break
  89. }
  90. }
  91. }
  92. }
  93. return m, cmd
  94. }
  95. func (m model) View() string {
  96. return m.subMenus[m.currentSubMenu].View()
  97. }
  98. func (m model) createPlugin() {
  99. permission := m.subMenus[SUB_MENU_KEY_PERMISSION].(permission).Permission()
  100. manifest := &plugin_entities.PluginDeclaration{
  101. PluginDeclarationWithoutAdvancedFields: plugin_entities.PluginDeclarationWithoutAdvancedFields{
  102. Version: manifest_entities.Version("0.0.1"),
  103. Type: manifest_entities.PluginType,
  104. Icon: "icon.svg",
  105. Author: m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Author(),
  106. Name: m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Name(),
  107. Description: plugin_entities.NewI18nObject(m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Description()),
  108. CreatedAt: time.Now(),
  109. Resource: plugin_entities.PluginResourceRequirement{
  110. Memory: 1024 * 1024 * 256, // 256MB
  111. Permission: &permission,
  112. },
  113. Label: plugin_entities.NewI18nObject(m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Name()),
  114. },
  115. }
  116. categoryString := m.subMenus[SUB_MENU_KEY_CATEGORY].(category).Category()
  117. if categoryString == "tool" {
  118. manifest.Plugins.Tools = []string{fmt.Sprintf("provider/%s.yaml", manifest.Name)}
  119. }
  120. if categoryString == "llm" ||
  121. categoryString == "text-embedding" ||
  122. categoryString == "speech2text" ||
  123. categoryString == "moderation" ||
  124. categoryString == "rerank" ||
  125. categoryString == "tts" {
  126. manifest.Plugins.Models = []string{fmt.Sprintf("provider/%s.yaml", manifest.Name)}
  127. }
  128. if categoryString == "extension" {
  129. manifest.Plugins.Endpoints = []string{fmt.Sprintf("group/%s.yaml", manifest.Name)}
  130. }
  131. manifest.Meta = plugin_entities.PluginMeta{
  132. Version: "0.0.1",
  133. Arch: []constants.Arch{
  134. constants.AMD64,
  135. constants.ARM64,
  136. },
  137. Runner: plugin_entities.PluginRunner{},
  138. }
  139. switch m.subMenus[SUB_MENU_KEY_LANGUAGE].(language).Language() {
  140. case constants.Python:
  141. manifest.Meta.Runner.Entrypoint = "main"
  142. manifest.Meta.Runner.Language = constants.Python
  143. manifest.Meta.Runner.Version = "3.12"
  144. default:
  145. log.Error("unsupported language: %s", m.subMenus[SUB_MENU_KEY_LANGUAGE].(language).Language())
  146. return
  147. }
  148. success := false
  149. clear := func() {
  150. if !success {
  151. os.RemoveAll(manifest.Name)
  152. }
  153. }
  154. defer clear()
  155. manifestFile := marshalYamlBytes(manifest)
  156. // create the plugin directory
  157. cwd, err := os.Getwd()
  158. if err != nil {
  159. log.Error("failed to get current working directory: %s", err)
  160. return
  161. }
  162. pluginDir := filepath.Join(cwd, manifest.Name)
  163. if err := writeFile(filepath.Join(pluginDir, "manifest.yaml"), string(manifestFile)); err != nil {
  164. log.Error("failed to write manifest file: %s", err)
  165. return
  166. }
  167. // create icon.svg
  168. if err := writeFile(filepath.Join(pluginDir, "_assets", "icon.svg"), string(icon)); err != nil {
  169. log.Error("failed to write icon file: %s", err)
  170. return
  171. }
  172. // create README.md
  173. readme, err := renderTemplate(README, manifest, []string{})
  174. if err != nil {
  175. log.Error("failed to render README template: %s", err)
  176. return
  177. }
  178. if err := writeFile(filepath.Join(pluginDir, "README.md"), readme); err != nil {
  179. log.Error("failed to write README file: %s", err)
  180. return
  181. }
  182. // create .env.example
  183. if err := writeFile(filepath.Join(pluginDir, ".env.example"), string(ENV_EXAMPLE)); err != nil {
  184. log.Error("failed to write .env.example file: %s", err)
  185. return
  186. }
  187. err = createPythonEnvironment(
  188. pluginDir,
  189. manifest.Meta.Runner.Entrypoint,
  190. manifest,
  191. m.subMenus[SUB_MENU_KEY_CATEGORY].(category).Category(),
  192. )
  193. if err != nil {
  194. log.Error("failed to create python environment: %s", err)
  195. return
  196. }
  197. success = true
  198. log.Info("plugin %s created successfully, you can refer to `%s/GUIDE.md` for more information about how to develop it", manifest.Name, manifest.Name)
  199. }