From 2b54814037d1d983b2db6d979060871fffe10187 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 31 Jul 2026 10:59:13 +0800 Subject: [PATCH] examples: update weather with v0.8.0 API surface, add luademo Lua example, fix go.mod replaces --- example/a2a/go.mod | 2 +- example/ai_image/go.mod | 2 +- example/bili/go.mod | 2 +- example/browser/go.mod | 8 +-- example/calendar/go.mod | 2 +- example/editdoc/go.mod | 2 +- example/files/go.mod | 2 +- example/luademo/README.md | 25 ++++++++++ example/luademo/main.lua | 101 ++++++++++++++++++++++++++++++++++++++ example/luademo/plg.json | 11 +++++ example/luademo/sdk.lua | 67 +++++++++++++++++++++++++ example/memo/go.mod | 2 +- example/music/go.mod | 2 +- example/ocr/go.mod | 2 +- example/rss/go.mod | 6 +-- example/sanitizer/go.mod | 2 +- example/weather/go.mod | 2 +- example/weather/plugin.go | 47 ++++++++++++++++++ 18 files changed, 269 insertions(+), 18 deletions(-) create mode 100644 example/luademo/README.md create mode 100644 example/luademo/main.lua create mode 100644 example/luademo/plg.json create mode 100644 example/luademo/sdk.lua diff --git a/example/a2a/go.mod b/example/a2a/go.mod index 15014f2..0365d02 100644 --- a/example/a2a/go.mod +++ b/example/a2a/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/ai_image/go.mod b/example/ai_image/go.mod index 5d0c6ae..32a87e4 100644 --- a/example/ai_image/go.mod +++ b/example/ai_image/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/bili/go.mod b/example/bili/go.mod index c0cc6c0..214ea61 100644 --- a/example/bili/go.mod +++ b/example/bili/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/browser/go.mod b/example/browser/go.mod index d484483..588f45d 100644 --- a/example/browser/go.mod +++ b/example/browser/go.mod @@ -17,10 +17,10 @@ require ( golang.org/x/sys v0.16.0 ) -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/calendar/go.mod b/example/calendar/go.mod index 941d1fe..55bd9f8 100644 --- a/example/calendar/go.mod +++ b/example/calendar/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/editdoc/go.mod b/example/editdoc/go.mod index 1d243f6..c0da724 100644 --- a/example/editdoc/go.mod +++ b/example/editdoc/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/files/go.mod b/example/files/go.mod index c483966..8383c79 100644 --- a/example/files/go.mod +++ b/example/files/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/luademo/README.md b/example/luademo/README.md new file mode 100644 index 0000000..0023514 --- /dev/null +++ b/example/luademo/README.md @@ -0,0 +1,25 @@ +# luademo + +Lua 插件全功能示例,展示 v0.8.0 Lua SDK 的完整能力面: + +- **工具注册**:`no_memory` + `cleaner`(记忆计算层过滤) +- **阶段钩子**:`register_stage(stage, handler, scope)`,`own_tools` 与全局作用域 +- **通道**:`register_output_channel` / `register_input_channel`(def 支持 no_memory/cleaner) +- **数据类 API**:`sdk.memory.*`、`sdk.doc.*`、`sdk.knowledge.*`、`sdk.text_memory.*`、`sdk.llm.*`、`sdk.settings.*`、`sdk.social.*` +- **其他**:`register_api`、`set_auto_restart` + +## 本地独立测试 + +```bash +lua main.lua # 使用 sdk.lua mock,不依赖内核 +``` + +## 构建 + +```bash +plugindev build +``` + +## 安装 + +通过插件管理 HTTP API 上传 `.hmap` 包,或解压到 `/plugins/luademo/` 后重启内核。 diff --git a/example/luademo/main.lua b/example/luademo/main.lua new file mode 100644 index 0000000..73370e8 --- /dev/null +++ b/example/luademo/main.lua @@ -0,0 +1,101 @@ +-- luademo plugin — 展示 v0.8.0 Lua SDK 全部能力 +-- 运行环境:内核注入真实实现;lua main.lua 可用 sdk.lua mock 独立测试 +local plugin = { name = "luademo" } + +function plugin.start(sdk) + sdk.log("info", "luademo starting...") + + -- 注册配置项(WebUI 可展示) + sdk.settings.register_def({ + key = "plugin.luademo.greeting", + default = "Hello", + type = "string", + display_name = "Greeting", + description = "Greeting prefix for the hello tool", + category = "luademo", + }) + + -- 注册工具:no_memory(输出跳过记忆计算)+ cleaner(计算层过滤函数) + sdk.register_tool("luademo_hello", { + description = "A hello world tool with no_memory and cleaner", + parameters = { type = "object", properties = {} }, + no_memory = true, + cleaner = function(text) return "CLEANED:" .. text end, + }, function(args) + local prefix, err = sdk.settings.get_core("plugin.luademo.greeting") + if err ~= nil then prefix = "Hello" end + return { content = (prefix or "Hello") .. " from luademo plugin!" } + end) + + -- 注册工具:数据类 API 巡检(memory/doc/knowledge/text_memory/llm/settings/social) + sdk.register_tool("luademo_probe", { + description = "Exercise every aligned data API and return combined results", + parameters = { type = "object", properties = {} }, + no_memory = true, + }, function(args) + local res = {} + + local ok, err = sdk.memory.commit({ { subject = "demo", relation = "uses", object = "lua" } }) + res.memory_commit = { ok = ok, err = err } + local recalled, rerr = sdk.memory.recall("demo", 1) + res.memory_recall = { result = recalled, err = rerr } + + ok, err = sdk.doc.insert({ id = "demo-1", title = "lua demo doc", content = "hello lua world" }) + res.doc_insert = { ok = ok, err = err } + local docs, derr = sdk.doc.query("lua", 2) + res.doc_query = { result = docs, err = derr } + + ok, err = sdk.knowledge.add("luademo", "lua knowledge entry") + res.knowledge_add = { ok = ok, err = err } + local entries, kerr = sdk.knowledge.search("luademo", 2) + res.knowledge_search = { result = entries, err = kerr } + + ok, err = sdk.text_memory.append({ role = "tool", content = "luademo probe ran", channel = "luademo" }) + res.text_memory = { ok = ok, err = err } + + local sources, serr = sdk.llm.list_sources() + res.llm_sources = { result = sources, err = serr } + + local v, verr = sdk.settings.get_core("agent.name") + res.settings_get_core = { result = v, err = verr } + local defs, defserr = sdk.settings.defs("plugin.luademo") + res.settings_defs = { result = defs, err = defserr } + + local persons, perr = sdk.social.list_persons() + res.social_persons = { result = persons, err = perr } + + return { content = res } + end) + + -- 阶段钩子:own_tools 作用域(仅本插件工具被调用时触发) + sdk.register_stage("before_toolcall", function(ctx) + local calls = ctx.tool_calls or {} + if calls[1] then + sdk.log("info", "luademo stage before_toolcall: tool=" .. tostring(calls[1].name)) + end + return nil + end, "own_tools") + + -- 阶段钩子:全局作用域 + sdk.register_stage("pre_action", function(ctx) + sdk.log("info", "luademo stage pre_action: user=" .. tostring(ctx.user_id)) + return nil + end) + + -- 输出通道:路由输出到外部渠道(def 支持 no_memory/cleaner) + sdk.register_output_channel("luademo_out", 0, "luademo push channel", + { no_memory = true, cleaner = function(t) return "OCLEANED:" .. t end }, + function(args) return { content = "out-channel ack" } end) + + -- 输入通道 + sdk.register_input_channel("luademo_in", { no_memory = true }) + + -- 其他 API + sdk.register_api("luademo.ping") + sdk.set_auto_restart(true) + + sdk.log("info", "luademo started") +end + +function plugin.stop() sdk.log("info", "luademo stopped") end +return plugin diff --git a/example/luademo/plg.json b/example/luademo/plg.json new file mode 100644 index 0000000..d605349 --- /dev/null +++ b/example/luademo/plg.json @@ -0,0 +1,11 @@ +{ + "name": "luademo", + "name_zh": "Lua 全功能示例", + "name_en": "Lua Demo", + "version": "0.1.0", + "description": "Lua 插件全功能示例:工具(no_memory/cleaner) + 阶段钩子 + 通道 + 数据类 API", + "author": "HomeAgent", + "entry": "main.lua", + "tags": ["luademo"], + "targets": "lua" +} diff --git a/example/luademo/sdk.lua b/example/luademo/sdk.lua new file mode 100644 index 0000000..3055151 --- /dev/null +++ b/example/luademo/sdk.lua @@ -0,0 +1,67 @@ +-- HomeAgent Lua Plugin SDK (standalone mock) +sdk = {} +function sdk.log(level, msg) print("[lua-plugin] " .. tostring(level) .. ": " .. tostring(msg)) end +function sdk.register_tool(name, def, handler) print("[lua-plugin] register_tool: " .. tostring(name)) end +function sdk.register_stage(stage, handler, scope) print("[lua-plugin] register_stage: " .. tostring(stage) .. " scope=" .. tostring(scope)) end +function sdk.register_api(name) print("[lua-plugin] register_api: " .. tostring(name)) end +function sdk.register_output_channel(name, caps, desc, def, handler) print("[lua-plugin] register_output_channel: " .. tostring(name)) end +function sdk.register_input_channel(name, def) print("[lua-plugin] register_input_channel: " .. tostring(name)) end +function sdk.get_setting(key) return nil end +function sdk.set_setting(key, value) print("[lua-plugin] set_setting: " .. tostring(key)) end +function sdk.inject_text(source, channel, text) print("[lua-plugin] inject_text: " .. tostring(source)) end +function sdk.inject_interrupt(source, channel, text) print("[lua-plugin] inject_interrupt: " .. tostring(source)) end +function sdk.inject_text_no_memory(source, channel, text) print("[lua-plugin] inject_text_no_memory: " .. tostring(source)) end +function sdk.set_auto_restart(enabled) print("[lua-plugin] set_auto_restart: " .. tostring(enabled)) end +sdk.memory = {} +function sdk.memory.recall(query, depth) return {entities={}, relations={}} end +function sdk.memory.commit(triples) return nil end +function sdk.memory.introspect() return {} end +function sdk.memory.merge(source, target) return 0 end +function sdk.memory.purge(criteria, hard) return 0 end +sdk.doc = {} +function sdk.doc.query(text, top_k) return {} end +function sdk.doc.insert(doc) return nil end +function sdk.doc.remove(id) return nil end +function sdk.doc.stats() return {} end +sdk.knowledge = {} +function sdk.knowledge.search(query, limit) return {} end +function sdk.knowledge.add(tag, content) return nil end +function sdk.knowledge.list() return {} end +sdk.text_memory = {} +function sdk.text_memory.append(evt) return nil end +sdk.llm = {} +function sdk.llm.list_sources() return {} end +function sdk.llm.set_source(name) return nil end +function sdk.llm.current_source() return nil end +sdk.social = {} +function sdk.social.get_person(name) return {} end +function sdk.social.get_network(name, depth) return {} end +function sdk.social.get_trait(name, trait) return {value=nil, found=false} end +function sdk.social.get_relations(name) return {} end +function sdk.social.list_persons() return {} end +sdk.settings = {} +function sdk.settings.get_core(key) return nil end +function sdk.settings.set_core(key, value) return nil end +function sdk.settings.list_core(prefix) return {} end +function sdk.settings.get_plugin(plugin, key) return nil end +function sdk.settings.set_plugin(plugin, key, value) return nil end +function sdk.settings.list_plugin(plugin, prefix) return {} end +function sdk.settings.list(prefix) return {} end +function sdk.settings.register_def(def) return nil end +function sdk.settings.defs(prefix) return {} end +function sdk.settings.dump() return {} end +function sdk.settings.plugins() return {} end +sdk.json = {} +function sdk.json.encode(val) + if type(val) == "string" then return '"' .. val:gsub('"', '\\"'):gsub('\n', '\\n') .. '"' + elseif type(val) == "number" or type(val) == "boolean" then return tostring(val) + elseif type(val) == "table" then local parts, i = {}, 1 + for k, v in pairs(val) do parts[i] = sdk.json.encode(k) .. ":" .. sdk.json.encode(v); i = i + 1 end + return "{" .. table.concat(parts, ",") .. "}" end + return "null" +end +function sdk.json.decode(str) local ok, fn = pcall(load, "return " .. str); if ok then return fn() end; return nil end +sdk.http = {} +function sdk.http.get(url) print("[lua-plugin] http.get: " .. tostring(url)); return {status=200, body='{"mock":true}', headers={}} end +function sdk.http.post(url, body, ct) print("[lua-plugin] http.post: " .. tostring(url)); return {status=200, body='{"mock":true}', headers={}} end +return sdk diff --git a/example/memo/go.mod b/example/memo/go.mod index ddd738f..5255f52 100644 --- a/example/memo/go.mod +++ b/example/memo/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/music/go.mod b/example/music/go.mod index 038962b..6410e30 100644 --- a/example/music/go.mod +++ b/example/music/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/ocr/go.mod b/example/ocr/go.mod index ebe7f3e..4bdc8d6 100644 --- a/example/ocr/go.mod +++ b/example/ocr/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/rss/go.mod b/example/rss/go.mod index 14abda9..5a92913 100644 --- a/example/rss/go.mod +++ b/example/rss/go.mod @@ -10,8 +10,8 @@ require ( golang.org/x/text v0.38.0 ) -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/sanitizer/go.mod b/example/sanitizer/go.mod index 5c20800..09b69cc 100644 --- a/example/sanitizer/go.mod +++ b/example/sanitizer/go.mod @@ -4,4 +4,4 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/weather/go.mod b/example/weather/go.mod index 65fb497..f561480 100644 --- a/example/weather/go.mod +++ b/example/weather/go.mod @@ -4,5 +4,5 @@ go 1.25.0 require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0 -replace gitcode.com/JianFeeeee/homeagent-sdk => E:/program/homeagent/homeagentsdk +replace gitcode.com/JianFeeeee/homeagent-sdk => ../../ diff --git a/example/weather/plugin.go b/example/weather/plugin.go index 089876c..ccafc44 100644 --- a/example/weather/plugin.go +++ b/example/weather/plugin.go @@ -60,6 +60,17 @@ func (p *Plugin) Start(s *sdk.PluginSDK) error { "units": map[string]interface{}{"type": "string", "description": "Units: metric (celsius) or imperial (fahrenheit), default metric"}, }, }, + // NoMemory: 外部实时数据对记忆计算无长期价值,跳过向量化/关键词提取 + NoMemory: true, + // Cleaner: 工具输出参与记忆计算前先过滤;这里演示用法(保留摘要行) + Cleaner: func(output string) string { + for _, line := range strings.Split(output, "\n") { + if strings.HasPrefix(line, "🌤") { + return line + } + } + return output + }, }, p.handleCurrent) s.RegisterTool(tp+"forecast", sdk.ToolDef{ @@ -72,6 +83,7 @@ func (p *Plugin) Start(s *sdk.PluginSDK) error { "units": map[string]interface{}{"type": "string", "description": "Units: metric or imperial, default metric"}, }, }, + NoMemory: true, }, p.handleForecast) s.RegisterTool(tp+"set_location", sdk.ToolDef{ @@ -83,8 +95,34 @@ func (p *Plugin) Start(s *sdk.PluginSDK) error { }, "required": []string{"location"}, }, + NoMemory: true, }, p.handleSetLocation) + // 阶段钩子:own_tools 作用域——仅在本插件的工具被调用时触发 + s.RegisterStage(sdk.StageAfterToolcall, func(ctx *sdk.StageContext) error { + ctx.Lock() + defer ctx.Unlock() + if len(ctx.ToolResults) > 0 { + fmt.Printf("[%s] stage after_toolcall(own): %s\n", p.name, ctx.ToolResults[0].Name) + } + return nil + }, sdk.StageScopeOwnTools) + + // 输出通道:把天气结果主动推给用户(如 QQ/WebUI 渠道) + if err := s.RegisterOutputChannel(tp+"weather_out", 0, "push weather to user", sdk.ChannelDef{ + NoMemory: true, + }, func(args map[string]interface{}) (interface{}, error) { + payload, _ := args["payload"].(string) + return map[string]interface{}{"content": "weather pushed: " + payload}, nil + }); err != nil { + return err + } + + // 输入通道:接收天气订阅请求(NoMemory: 通道输入不参与记忆计算) + if err := s.RegisterInputChannel(tp+"weather_in", sdk.ChannelDef{NoMemory: true}); err != nil { + return err + } + fmt.Printf("[%s] started\n", p.name) return nil } @@ -256,6 +294,15 @@ func (p *Plugin) handleCurrent(args map[string]interface{}) (interface{}, error) cc.Humidity, cc.WindspeedKmph, windUnit, cc.Winddir16Point, obsTime) + // 文本记忆:每次查询写入一条历史记录(role=tool 便于追溯) + if p.sdk.TextMemory() != nil { + _ = p.sdk.TextMemory().Append(sdk.TextEvent{ + Role: "tool", + Content: fmt.Sprintf("weather %s: %s", place, desc), + Channel: p.name, + }) + } + return map[string]interface{}{ "content": result, "location": place,