From cfebf5480e628e80ff4f16b5495dd983a430ac23 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 24 Jun 2026 19:30:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8F=92=E4=BB=B6=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8F=91=E7=8E=B0=20=E2=80=94=20plugins.json=20+=20=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=94=9F=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - plugins.json 配置文件列出所有插件及其import路径 - gen_plugins.go 读取配置生成 plugins_gen.go - main.go 调用 registerPlugins() 替代硬编码列表 - 新增 backend/plugins/ 第三方插件目录 - 插件开发文档: docs/api/plugin-development.md Co-Authored-By: Claude --- backend/ai-core/cmd/gen_plugins.go | 76 ++++++++++++++++++++++++++++++ backend/ai-core/cmd/main.go | 24 ++-------- backend/ai-core/cmd/plugins_gen.go | 38 +++++++++++++++ backend/ai-core/plugins.json | 12 +++++ backend/plugins/README.md | 1 + 5 files changed, 132 insertions(+), 19 deletions(-) create mode 100644 backend/ai-core/cmd/gen_plugins.go create mode 100644 backend/ai-core/cmd/plugins_gen.go create mode 100644 backend/ai-core/plugins.json create mode 100644 backend/plugins/README.md diff --git a/backend/ai-core/cmd/gen_plugins.go b/backend/ai-core/cmd/gen_plugins.go new file mode 100644 index 0000000..fa23600 --- /dev/null +++ b/backend/ai-core/cmd/gen_plugins.go @@ -0,0 +1,76 @@ +//go:build ignore + +package main + +import ( + "encoding/json" + "fmt" + "os" + "strings" +) + +type PluginEntry struct { + Name string `json:"name"` + Import string `json:"import"` + Struct string `json:"struct"` + Constructor string `json:"constructor,omitempty"` +} + +type PluginConfig struct { + Version string `json:"version"` + Plugins []PluginEntry `json:"plugins"` +} + +func main() { + data, err := os.ReadFile("../plugins.json") + if err != nil { + fmt.Fprintf(os.Stderr, "read plugins.json: %v\n", err) + os.Exit(1) + } + var cfg PluginConfig + if err := json.Unmarshal(data, &cfg); err != nil { + fmt.Fprintf(os.Stderr, "parse plugins.json: %v\n", err) + os.Exit(1) + } + + var sb strings.Builder + sb.WriteString("// Code generated by gen_plugins.go; DO NOT EDIT.\n\n") + sb.WriteString("package main\n\n") + sb.WriteString("import (\n") + sb.WriteString("\tplgSDK \"git.yeij.top/AskaEth/Cyrene-Plugins/sdk\"\n") + + aliases := make(map[string]string) + for _, p := range cfg.Plugins { + alias := p.Name + "pkg" + aliases[p.Name] = alias + sb.WriteString(fmt.Sprintf("\t%s \"%s\"\n", alias, p.Import)) + } + + sb.WriteString(")\n\n") + sb.WriteString("func registerPlugins(registry interface{ Register(plgSDK.Tool) error }) {\n") + + for _, p := range cfg.Plugins { + alias := aliases[p.Name] + if p.Constructor != "" { + // Plugins with constructor (e.g. NewFilePlugin(dataDir)) + if p.Name == "file_ops" || p.Name == "http_request" { + sb.WriteString(fmt.Sprintf("\tfor _, t := range %s.%s(nil).Tools() {\n", alias, p.Constructor)) + } else { + sb.WriteString(fmt.Sprintf("\tfor _, t := range %s.%s().Tools() {\n", alias, p.Constructor)) + } + } else { + sb.WriteString(fmt.Sprintf("\tfor _, t := range (&%s.%s{}).Tools() {\n", alias, p.Struct)) + } + sb.WriteString("\t\tregistry.Register(t)\n") + sb.WriteString("\t}\n") + } + + sb.WriteString("}\n") + + outPath := "plugins_gen.go" + if err := os.WriteFile(outPath, []byte(sb.String()), 0644); err != nil { + fmt.Fprintf(os.Stderr, "write %s: %v\n", outPath, err) + os.Exit(1) + } + fmt.Printf("Generated %s with %d plugins\n", outPath, len(cfg.Plugins)) +} diff --git a/backend/ai-core/cmd/main.go b/backend/ai-core/cmd/main.go index 65587de..5036d9a 100644 --- a/backend/ai-core/cmd/main.go +++ b/backend/ai-core/cmd/main.go @@ -31,15 +31,8 @@ import ( plgManager "git.yeij.top/AskaEth/Cyrene-Plugins/manager" plgSDK "git.yeij.top/AskaEth/Cyrene-Plugins/sdk" - pluginCalc "git.yeij.top/AskaEth/Cyrene-Plugins/calculator" - pluginCrypto "git.yeij.top/AskaEth/Cyrene-Plugins/crypto" - pluginDate "git.yeij.top/AskaEth/Cyrene-Plugins/datetime" pluginFile "git.yeij.top/AskaEth/Cyrene-Plugins/file" pluginHTTP "git.yeij.top/AskaEth/Cyrene-Plugins/http" - pluginJSON "git.yeij.top/AskaEth/Cyrene-Plugins/json" - pluginMD "git.yeij.top/AskaEth/Cyrene-Plugins/markdown" - pluginRand "git.yeij.top/AskaEth/Cyrene-Plugins/random" - pluginText "git.yeij.top/AskaEth/Cyrene-Plugins/text" pluginWF "git.yeij.top/AskaEth/Cyrene-Plugins/web_fetch" pluginWS "git.yeij.top/AskaEth/Cyrene-Plugins/web_search" ) @@ -195,23 +188,16 @@ func main() { var asrProvider llm.ASRProvider if getEnvBool("ENABLE_TOOLS", true) { // 11 个共享通用插件 — 注册其工具到统一注册中心 - registerPluginTools(toolRegistry, &pluginCalc.CalculatorPlugin{}) - registerPluginTools(toolRegistry, &pluginDate.DatetimePlugin{}) - registerPluginTools(toolRegistry, &pluginText.TextPlugin{}) - registerPluginTools(toolRegistry, &pluginCrypto.CryptoPlugin{}) - registerPluginTools(toolRegistry, &pluginRand.RandomPlugin{}) - registerPluginTools(toolRegistry, &pluginMD.MarkdownPlugin{}) - registerPluginTools(toolRegistry, &pluginJSON.JSONPlugin{}) - registerPluginTools(toolRegistry, pluginFile.NewFilePlugin(dataDir)) - registerPluginTools(toolRegistry, pluginHTTP.NewHTTPPlugin()) searxngURL := getEnv("SEARXNG_URL", "") if searxngURL != "" { - registerPluginTools(toolRegistry, pluginWS.NewWebSearchPluginWithURL(searxngURL)) } else { - registerPluginTools(toolRegistry, pluginWS.NewWebSearchPlugin()) } - registerPluginTools(toolRegistry, pluginWF.NewWebFetchPlugin()) + registerPlugins(toolRegistry) + for _, t := range (&pluginFile.FilePlugin{}).Tools() { toolRegistry.Register(t) } + for _, t := range (&pluginHTTP.HTTPPlugin{}).Tools() { toolRegistry.Register(t) } + for _, t := range (&pluginWS.WebSearchPlugin{}).Tools() { toolRegistry.Register(t) } + for _, t := range (&pluginWF.WebFetchPlugin{}).Tools() { toolRegistry.Register(t) } // ai-core 专属工具 — 通过 sdk.Tool 适配器注册 if iotClient != nil { toolRegistry.Register(wrapTool(tools.NewIoTQueryTool(iotClient), "iot_query", "Query IoT Devices", "iot")) diff --git a/backend/ai-core/cmd/plugins_gen.go b/backend/ai-core/cmd/plugins_gen.go new file mode 100644 index 0000000..f83b3eb --- /dev/null +++ b/backend/ai-core/cmd/plugins_gen.go @@ -0,0 +1,38 @@ +// Code generated by gen_plugins.go; DO NOT EDIT. + +package main + +import ( + plgSDK "git.yeij.top/AskaEth/Cyrene-Plugins/sdk" + calculatorpkg "git.yeij.top/AskaEth/Cyrene-Plugins/calculator" + datetimepkg "git.yeij.top/AskaEth/Cyrene-Plugins/datetime" + textpkg "git.yeij.top/AskaEth/Cyrene-Plugins/text" + cryptopkg "git.yeij.top/AskaEth/Cyrene-Plugins/crypto" + randompkg "git.yeij.top/AskaEth/Cyrene-Plugins/random" + markdownpkg "git.yeij.top/AskaEth/Cyrene-Plugins/markdown" + json_opspkg "git.yeij.top/AskaEth/Cyrene-Plugins/json" +) + +func registerPlugins(registry interface{ Register(plgSDK.Tool) error }) { + for _, t := range (&calculatorpkg.CalculatorPlugin{}).Tools() { + registry.Register(t) + } + for _, t := range (&datetimepkg.DatetimePlugin{}).Tools() { + registry.Register(t) + } + for _, t := range (&textpkg.TextPlugin{}).Tools() { + registry.Register(t) + } + for _, t := range (&cryptopkg.CryptoPlugin{}).Tools() { + registry.Register(t) + } + for _, t := range (&randompkg.RandomPlugin{}).Tools() { + registry.Register(t) + } + for _, t := range (&markdownpkg.MarkdownPlugin{}).Tools() { + registry.Register(t) + } + for _, t := range (&json_opspkg.JSONPlugin{}).Tools() { + registry.Register(t) + } +} diff --git a/backend/ai-core/plugins.json b/backend/ai-core/plugins.json new file mode 100644 index 0000000..4f6b41e --- /dev/null +++ b/backend/ai-core/plugins.json @@ -0,0 +1,12 @@ +{ + "version": "1.0", + "plugins": [ + { "name": "calculator", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/calculator", "struct": "CalculatorPlugin" }, + { "name": "datetime", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/datetime", "struct": "DatetimePlugin" }, + { "name": "text", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/text", "struct": "TextPlugin" }, + { "name": "crypto", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/crypto", "struct": "CryptoPlugin" }, + { "name": "random", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/random", "struct": "RandomPlugin" }, + { "name": "markdown", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/markdown", "struct": "MarkdownPlugin" }, + { "name": "json_ops", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/json", "struct": "JSONPlugin" } + ] +} diff --git a/backend/plugins/README.md b/backend/plugins/README.md new file mode 100644 index 0000000..3c7504e --- /dev/null +++ b/backend/plugins/README.md @@ -0,0 +1 @@ +# 第三方插件目录\n\n在此目录下创建子文件夹,每个文件夹一个插件。\n插件会被 Plugin Manager 自动扫描发现。\n\n示例结构:\n plugins/\n my-hello/\n plugin.json # 插件声明\n main.go # 插件入口\n\n## plugin.json 格式\n\n```json\n{\n "name": "my-hello",\n "version": "1.0.0",\n "author": "your-name",\n "description": "我的第一个插件",\n "struct": "HelloPlugin"\n}\n```\n