init.go 5.9 KB

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