瀏覽代碼

feat: add agent template to cli

Yeuoly 7 月之前
父節點
當前提交
e91068d61f

+ 5 - 2
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",

+ 24 - 1
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{

+ 1 - 1
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 {

+ 11 - 3
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 {

+ 15 - 1
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
 }

+ 31 - 0
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
+}

+ 0 - 1
cmd/commandline/plugin/templates/README.md

@@ -2,7 +2,6 @@
 
 **Author:** {{ .Author }}
 **Version:** {{ .Version }}
-**Date:** {{ .Date }}
 **Type:** {{ .Category }}
 
 ### Description

+ 13 - 0
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

+ 11 - 0
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

+ 26 - 0
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

+ 4 - 3
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 {