mirror of
https://github.com/langgenius/dify-plugin-daemon.git
synced 2026-07-22 17:56:00 -04:00
266 lines
7.2 KiB
Go
266 lines
7.2 KiB
Go
package plugin
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
_ "embed"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/utils/log"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/entities/constants"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/entities/manifest_entities"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
|
|
)
|
|
|
|
//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()),
|
|
Privacy: parser.ToPtr("PRIVACY.md"),
|
|
},
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// create PRIVACY.md
|
|
if err := writeFile(filepath.Join(pluginDir, "PRIVACY.md"), string(PRIVACY)); err != nil {
|
|
log.Error("failed to write PRIVACY 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)
|
|
}
|