123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- package plugin
- import (
- "fmt"
- "os"
- "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/manifest_entities"
- "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)
- if result, err := p.Run(); err != nil {
- fmt.Println("Error running program:", err)
- } else {
- if m, ok := result.(model); ok {
- if m.completed {
- m.createPlugin()
- }
- } else {
- log.Error("Error running program:", err)
- return
- }
- }
- }
- type subMenuKey string
- const (
- SUB_MENU_KEY_PROFILE subMenuKey = "profile"
- SUB_MENU_KEY_LANGUAGE subMenuKey = "language"
- SUB_MENU_KEY_CATEGORY subMenuKey = "category"
- SUB_MENU_KEY_PERMISSION subMenuKey = "permission"
- )
- type model struct {
- subMenus map[subMenuKey]subMenu
- subMenuSeq []subMenuKey
- currentSubMenu subMenuKey
- completed bool
- }
- func initialize() model {
- m := model{}
- m.subMenus = map[subMenuKey]subMenu{
- SUB_MENU_KEY_PROFILE: newProfile(),
- SUB_MENU_KEY_LANGUAGE: newLanguage(),
- SUB_MENU_KEY_CATEGORY: newCategory(),
- SUB_MENU_KEY_PERMISSION: newPermission(plugin_entities.PluginPermissionRequirement{}),
- }
- m.currentSubMenu = SUB_MENU_KEY_PROFILE
- m.subMenuSeq = []subMenuKey{
- SUB_MENU_KEY_PROFILE,
- SUB_MENU_KEY_LANGUAGE,
- SUB_MENU_KEY_CATEGORY,
- SUB_MENU_KEY_PERMISSION,
- }
- return m
- }
- func (m model) Init() tea.Cmd {
- return nil
- }
- func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- currentSubMenu, event, cmd := m.subMenus[m.currentSubMenu].Update(msg)
- m.subMenus[m.currentSubMenu] = currentSubMenu
- switch event {
- case SUB_MENU_EVENT_NEXT:
- if m.currentSubMenu != m.subMenuSeq[len(m.subMenuSeq)-1] {
- // move the current sub menu to the next one
- for i, key := range m.subMenuSeq {
- if key == m.currentSubMenu {
- // check if the next sub menu is permission
- if key == SUB_MENU_KEY_CATEGORY {
- // get the type of current category
- category := m.subMenus[SUB_MENU_KEY_CATEGORY].(category).Category()
- if category == "agent-strategy" {
- // update the permission to add tool and model invocation
- perm := m.subMenus[SUB_MENU_KEY_PERMISSION].(permission)
- perm.UpdatePermission(plugin_entities.PluginPermissionRequirement{
- Tool: &plugin_entities.PluginPermissionToolRequirement{
- Enabled: true,
- },
- Model: &plugin_entities.PluginPermissionModelRequirement{
- Enabled: true,
- LLM: true,
- },
- })
- m.subMenus[SUB_MENU_KEY_PERMISSION] = perm
- }
- }
- m.currentSubMenu = m.subMenuSeq[i+1]
- break
- }
- }
- } else {
- m.completed = true
- return m, tea.Quit
- }
- case SUB_MENU_EVENT_PREV:
- if m.currentSubMenu != m.subMenuSeq[0] {
- // move the current sub menu to the previous one
- for i, key := range m.subMenuSeq {
- if key == m.currentSubMenu {
- m.currentSubMenu = m.subMenuSeq[i-1]
- break
- }
- }
- }
- }
- return m, cmd
- }
- func (m model) View() string {
- return m.subMenus[m.currentSubMenu].View()
- }
- func (m model) createPlugin() {
- permission := m.subMenus[SUB_MENU_KEY_PERMISSION].(permission).Permission()
- manifest := &plugin_entities.PluginDeclaration{
- PluginDeclarationWithoutAdvancedFields: plugin_entities.PluginDeclarationWithoutAdvancedFields{
- Version: manifest_entities.Version("0.0.1"),
- Type: manifest_entities.PluginType,
- Icon: "icon.svg",
- Author: m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Author(),
- Name: m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Name(),
- Description: plugin_entities.NewI18nObject(m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Description()),
- CreatedAt: time.Now(),
- Resource: plugin_entities.PluginResourceRequirement{
- Memory: 1024 * 1024 * 256, // 256MB
- Permission: &permission,
- },
- Label: plugin_entities.NewI18nObject(m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Name()),
- },
- }
- categoryString := m.subMenus[SUB_MENU_KEY_CATEGORY].(category).Category()
- if categoryString == "tool" {
- manifest.Plugins.Tools = []string{fmt.Sprintf("provider/%s.yaml", manifest.Name)}
- }
- if categoryString == "llm" ||
- categoryString == "text-embedding" ||
- categoryString == "speech2text" ||
- categoryString == "moderation" ||
- categoryString == "rerank" ||
- categoryString == "tts" {
- manifest.Plugins.Models = []string{fmt.Sprintf("provider/%s.yaml", manifest.Name)}
- }
- if categoryString == "extension" {
- manifest.Plugins.Endpoints = []string{fmt.Sprintf("group/%s.yaml", manifest.Name)}
- }
- if categoryString == "agent-strategy" {
- manifest.Plugins.AgentStrategies = []string{fmt.Sprintf("provider/%s.yaml", manifest.Name)}
- }
- manifest.Meta = plugin_entities.PluginMeta{
- Version: "0.0.1",
- Arch: []constants.Arch{
- constants.AMD64,
- constants.ARM64,
- },
- Runner: plugin_entities.PluginRunner{},
- }
- switch m.subMenus[SUB_MENU_KEY_LANGUAGE].(language).Language() {
- case constants.Python:
- manifest.Meta.Runner.Entrypoint = "main"
- manifest.Meta.Runner.Language = constants.Python
- manifest.Meta.Runner.Version = "3.12"
- default:
- log.Error("unsupported language: %s", m.subMenus[SUB_MENU_KEY_LANGUAGE].(language).Language())
- return
- }
- success := false
- clear := func() {
- if !success {
- os.RemoveAll(manifest.Name)
- }
- }
- defer clear()
- manifestFile := marshalYamlBytes(manifest)
- // create the plugin directory
- cwd, err := os.Getwd()
- if err != nil {
- log.Error("failed to get current working directory: %s", err)
- return
- }
- pluginDir := filepath.Join(cwd, manifest.Name)
- if err := writeFile(filepath.Join(pluginDir, "manifest.yaml"), string(manifestFile)); err != nil {
- log.Error("failed to write manifest file: %s", err)
- return
- }
- // create icon.svg
- if err := writeFile(filepath.Join(pluginDir, "_assets", "icon.svg"), string(icon)); err != nil {
- log.Error("failed to write icon file: %s", err)
- return
- }
- // create README.md
- readme, err := renderTemplate(README, manifest, []string{})
- if err != nil {
- log.Error("failed to render README template: %s", err)
- return
- }
- if err := writeFile(filepath.Join(pluginDir, "README.md"), readme); err != nil {
- log.Error("failed to write README file: %s", err)
- return
- }
- // create .env.example
- if err := writeFile(filepath.Join(pluginDir, ".env.example"), string(ENV_EXAMPLE)); err != nil {
- log.Error("failed to write .env.example file: %s", err)
- return
- }
- err = createPythonEnvironment(
- pluginDir,
- manifest.Meta.Runner.Entrypoint,
- manifest,
- m.subMenus[SUB_MENU_KEY_CATEGORY].(category).Category(),
- )
- if err != nil {
- log.Error("failed to create python environment: %s", err)
- return
- }
- success = true
- 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)
- }
|