diff --git a/cmd/commandline/plugin/category.go b/cmd/commandline/plugin/category.go index f0d6cc5..641ee2a 100644 --- a/cmd/commandline/plugin/category.go +++ b/cmd/commandline/plugin/category.go @@ -21,13 +21,15 @@ const PLUGIN_GUIDE = `But before starting, you need some basic knowledge about t ` + "\n" + BOLD + `- Tool` + RESET + `: ` + GREEN + `Tool Providers like Google Search, Stable Diffusion, etc. it can be used to perform a specific task.` + RESET + ` ` + BOLD + `- Model` + RESET + `: ` + GREEN + `Model Providers like OpenAI, Anthropic, etc. you can use their models to enhance the AI capabilities.` + RESET + ` ` + BOLD + `- Endpoint` + RESET + `: ` + GREEN + `Like Service API in Dify and Ingress in Kubernetes, you can extend a http service as an endpoint and control its logics using your own code.` + RESET + ` +` + BOLD + `- Agent Strategy` + RESET + `: ` + GREEN + `You can implement your own agent strategy like Function Calling, ReAct, ToT, Cot, etc. anyway you want.` + RESET + ` -Based on the ability you want to extend, we have divided the Plugin into three types: ` + BOLD + `Tool` + RESET + `, ` + BOLD + `Model` + RESET + `, and ` + BOLD + `Extension` + RESET + `. +Based on the ability you want to extend, we have divided the Plugin into four types: ` + BOLD + `Tool` + RESET + `, ` + BOLD + `Model` + RESET + `, ` + BOLD + `Extension` + RESET + `, and ` + BOLD + `Agent Strategy` + RESET + `. ` + BOLD + `- Tool` + RESET + `: ` + YELLOW + `It's a tool provider, but not only limited to tools, you can implement an endpoint there, for example, you need both ` + BLUE + `Sending Message` + RESET + YELLOW + ` and ` + BLUE + `Receiving Message` + RESET + YELLOW + ` if you are building a Discord Bot, ` + BOLD + `Tool` + RESET + YELLOW + ` and ` + BOLD + `Endpoint` + RESET + YELLOW + ` are both required.` + RESET + ` ` + BOLD + `- Model` + RESET + `: ` + YELLOW + `Just a model provider, extending others is not allowed.` + RESET + ` ` + BOLD + `- Extension` + RESET + `: ` + YELLOW + `Other times, you may only need a simple http service to extend the functionalities, ` + BOLD + `Extension` + RESET + YELLOW + ` is the right choice for you.` + RESET + ` -` + ` +` + BOLD + `- Agent Strategy` + RESET + `: ` + YELLOW + `Implement your own logics here, just by focusing on Agent itself` + RESET + ` + What's more, we have provided the template for you, you can choose one of them below: ` @@ -37,6 +39,7 @@ type category struct { var categories = []string{ "tool", + "agent-strategy", "llm", "text-embedding", "rerank", diff --git a/cmd/commandline/plugin/init.go b/cmd/commandline/plugin/init.go index a05e3e5..312e056 100644 --- a/cmd/commandline/plugin/init.go +++ b/cmd/commandline/plugin/init.go @@ -58,7 +58,7 @@ func initialize() model { SUB_MENU_KEY_PROFILE: newProfile(), SUB_MENU_KEY_LANGUAGE: newLanguage(), SUB_MENU_KEY_CATEGORY: newCategory(), - SUB_MENU_KEY_PERMISSION: newPermission(), + SUB_MENU_KEY_PERMISSION: newPermission(plugin_entities.PluginPermissionRequirement{}), } m.currentSubMenu = SUB_MENU_KEY_PROFILE @@ -86,6 +86,25 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // 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 } @@ -151,6 +170,10 @@ func (m model) createPlugin() { 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{ diff --git a/cmd/commandline/plugin/language.go b/cmd/commandline/plugin/language.go index 636ecb4..fbf38de 100644 --- a/cmd/commandline/plugin/language.go +++ b/cmd/commandline/plugin/language.go @@ -29,7 +29,7 @@ func (l language) Language() constants.Language { func (l language) View() string { s := `Select the language you want to use for plugin development, and press ` + GREEN + `Enter` + RESET + ` to continue, -BTW, you need Python 3.10+ to develop the Plugin if you choose Python. +BTW, you need Python 3.12+ to develop the Plugin if you choose Python. ` for i, language := range languages { if i == l.cursor { diff --git a/cmd/commandline/plugin/permission.go b/cmd/commandline/plugin/permission.go index 5722c5f..e9936b6 100644 --- a/cmd/commandline/plugin/permission.go +++ b/cmd/commandline/plugin/permission.go @@ -37,9 +37,10 @@ type permission struct { storageSizeEditor ti.Model } -func newPermission() permission { +func newPermission(defaultPermission plugin_entities.PluginPermissionRequirement) permission { return permission{ cursor: permissionKeySeq[0], + permission: defaultPermission, storageSizeEditor: ti.New(), } } @@ -285,6 +286,10 @@ func (p permission) Update(msg tea.Msg) (subMenu, subMenuEvent, tea.Cmd) { return p, SUB_MENU_EVENT_NONE, nil } +func (p *permission) UpdatePermission(permission plugin_entities.PluginPermissionRequirement) { + p.permission = permission +} + func (p permission) Init() tea.Cmd { return nil } @@ -322,11 +327,14 @@ func EditPermission(pluginPath string) { return } + if manifest.Resource.Permission == nil { + manifest.Resource.Permission = &plugin_entities.PluginPermissionRequirement{} + } + // create a new permission m := permissionModel{ - permission: newPermission(), + permission: newPermission(*manifest.Resource.Permission), } - m.permission.permission = *manifest.Resource.Permission p := tea.NewProgram(m) if result, err := p.Run(); err != nil { diff --git a/cmd/commandline/plugin/python.go b/cmd/commandline/plugin/python.go index b048f45..85dfcd7 100644 --- a/cmd/commandline/plugin/python.go +++ b/cmd/commandline/plugin/python.go @@ -82,6 +82,15 @@ var PYTHON_ENDPOINT_TEMPLATE []byte //go:embed templates/python/endpoint.yaml var PYTHON_ENDPOINT_MANIFEST_TEMPLATE []byte +//go:embed templates/python/agent_provider.yaml +var PYTHON_AGENT_PROVIDER_MANIFEST_TEMPLATE []byte + +//go:embed templates/python/agent_strategy.yaml +var PYTHON_AGENT_STRATEGY_MANIFEST_TEMPLATE []byte + +//go:embed templates/python/agent_strategy.py +var PYTHON_AGENT_STRATEGY_TEMPLATE []byte + //go:embed templates/python/GUIDE.md var PYTHON_GUIDE []byte @@ -102,7 +111,6 @@ func renderTemplate( "PluginDescription": manifest.Description.EnUS, "SupportedModelTypes": supported_model_types, "Version": manifest.Version, - "Date": manifest.CreatedAt, "Category": manifest.Category(), }); err != nil { return "", err @@ -203,5 +211,11 @@ func createPythonEnvironment( } } + if category == "agent-strategy" { + if err := createPythonAgentStrategy(root, manifest); err != nil { + return err + } + } + return nil } diff --git a/cmd/commandline/plugin/python_categories.go b/cmd/commandline/plugin/python_categories.go index 3cd5380..c84e0d8 100644 --- a/cmd/commandline/plugin/python_categories.go +++ b/cmd/commandline/plugin/python_categories.go @@ -239,3 +239,34 @@ func createPythonModelProvider(root string, manifest *plugin_entities.PluginDecl return nil } + +func createPythonAgentStrategy(root string, manifest *plugin_entities.PluginDeclaration) error { + agentFileContent, err := renderTemplate(PYTHON_AGENT_PROVIDER_MANIFEST_TEMPLATE, manifest, []string{"agent"}) + if err != nil { + return err + } + agentFilePath := filepath.Join(root, "provider", fmt.Sprintf("%s.yaml", manifest.Name)) + if err := writeFile(agentFilePath, agentFileContent); err != nil { + return err + } + + agentStrategyFileContent, err := renderTemplate(PYTHON_AGENT_STRATEGY_MANIFEST_TEMPLATE, manifest, []string{"agent"}) + if err != nil { + return err + } + agentStrategyFilePath := filepath.Join(root, "strategies", fmt.Sprintf("%s.yaml", manifest.Name)) + if err := writeFile(agentStrategyFilePath, agentStrategyFileContent); err != nil { + return err + } + + agentStrategyPyFileContent, err := renderTemplate(PYTHON_AGENT_STRATEGY_TEMPLATE, manifest, []string{"agent"}) + if err != nil { + return err + } + agentStrategyPyFilePath := filepath.Join(root, "strategies", fmt.Sprintf("%s.py", manifest.Name)) + if err := writeFile(agentStrategyPyFilePath, agentStrategyPyFileContent); err != nil { + return err + } + + return nil +} diff --git a/cmd/commandline/plugin/templates/README.md b/cmd/commandline/plugin/templates/README.md index 288e641..e41eebd 100644 --- a/cmd/commandline/plugin/templates/README.md +++ b/cmd/commandline/plugin/templates/README.md @@ -2,7 +2,6 @@ **Author:** {{ .Author }} **Version:** {{ .Version }} -**Date:** {{ .Date }} **Type:** {{ .Category }} ### Description diff --git a/cmd/commandline/plugin/templates/python/agent_provider.yaml b/cmd/commandline/plugin/templates/python/agent_provider.yaml new file mode 100644 index 0000000..87ad4de --- /dev/null +++ b/cmd/commandline/plugin/templates/python/agent_provider.yaml @@ -0,0 +1,13 @@ +identity: + author: {{ .Author }} + name: {{ .PluginName }} + label: + en_US: {{ .PluginName | SnakeToCamel }} + description: + en_US: {{ .PluginName | SnakeToCamel }} + icon: icon.svg +strategies: + - strategies/{{ .PluginName }}.yaml +extra: + python: + source: provider/{{ .PluginName }}.py diff --git a/cmd/commandline/plugin/templates/python/agent_strategy.py b/cmd/commandline/plugin/templates/python/agent_strategy.py new file mode 100644 index 0000000..f279264 --- /dev/null +++ b/cmd/commandline/plugin/templates/python/agent_strategy.py @@ -0,0 +1,11 @@ +from collections.abc import Generator +from typing import Any + + +from dify_plugin.entities.agent import AgentInvokeMessage +from dify_plugin.interfaces.agent import AgentStrategy + + +class {{ .PluginName | SnakeToCamel }}AgentStrategy(AgentStrategy): + def _invoke(self, parameters: dict[str, Any]) -> Generator[AgentInvokeMessage]: + pass \ No newline at end of file diff --git a/cmd/commandline/plugin/templates/python/agent_strategy.yaml b/cmd/commandline/plugin/templates/python/agent_strategy.yaml new file mode 100644 index 0000000..2bac446 --- /dev/null +++ b/cmd/commandline/plugin/templates/python/agent_strategy.yaml @@ -0,0 +1,26 @@ +identity: + name: {{ .PluginName }} + author: {{ .Author }} + label: + en_US: {{ .PluginName | SnakeToCamel }} +description: + en_US: {{ .PluginName | SnakeToCamel }} +parameters: + - name: model + type: model-selector + scope: tool-call&llm + required: true + label: + en_US: Model + zh_Hans: 模型 + pt_BR: Model + - name: tools + type: array[tools] + required: true + label: + en_US: Tools list + zh_Hans: 工具列表 + pt_BR: Tools list +extra: + python: + source: strategies/{{ .PluginName }}.py diff --git a/internal/types/entities/plugin_entities/plugin_declaration.go b/internal/types/entities/plugin_entities/plugin_declaration.go index 930532f..5e9c757 100644 --- a/internal/types/entities/plugin_entities/plugin_declaration.go +++ b/internal/types/entities/plugin_entities/plugin_declaration.go @@ -132,9 +132,10 @@ type PluginMeta struct { } type PluginExtensions struct { - Tools []string `json:"tools" yaml:"tools,omitempty" validate:"omitempty,dive,max=128"` - Models []string `json:"models" yaml:"models,omitempty" validate:"omitempty,dive,max=128"` - Endpoints []string `json:"endpoints" yaml:"endpoints,omitempty" validate:"omitempty,dive,max=128"` + Tools []string `json:"tools" yaml:"tools,omitempty" validate:"omitempty,dive,max=128"` + Models []string `json:"models" yaml:"models,omitempty" validate:"omitempty,dive,max=128"` + Endpoints []string `json:"endpoints" yaml:"endpoints,omitempty" validate:"omitempty,dive,max=128"` + AgentStrategies []string `json:"agent_strategies" yaml:"agent_strategies,omitempty" validate:"omitempty,dive,max=128"` } type PluginDeclarationWithoutAdvancedFields struct {