mirror of
https://gitcode.com/JianFeeeee/homeagent-sdk.git
synced 2026-09-20 08:58:03 +00:00
内核侧 Lua 桥此前停在 v0.8.0 时代能力面,而 1.1/1.2/1.3 新增的 媒体、注入标志位、中断优先级、事件订阅、动态通道注销只在 Go 侧存在, 文档却宣称『能力完全对齐』——属于静默漂移。 本仓(事实源): - 新增 sdk/lua/sdk.lua:Lua mock 的单一事实源,补齐全部新 API (*_opts / inject_input_sync / inject_*_media / set_tool_blocks / unregister_output_channel / events / plugin_mgr / insert_with_media / sentence_text+media_digests / attachments / context_policy)。 - scripts/sync-lua-sdk.sh:把事实源同步到 hmapdev assets、luademo、 以及被 vendored 时的内核副本;三份 sdk.lua 不再各自漂移。 - hmapdev init --lua:优先从激活 SDK 拷权威 mock,内嵌模板降级为 assets/sdk.lua 回退,不再内联手写副本。 - hmapdev build(Lua):plugin.json 写入 SDK 版本(能力可追溯), 打包前用 luac -p / lua loadfile 做语法预检,失败以非零码退出。 - hmapdev debug --lua:优先用激活 SDK 的权威 mock(HMAPDEV_SDK_LUA)。 - luademo 升级为全能力示例(新增 luademo_probe_v2)。
165 lines
6.5 KiB
Lua
165 lines
6.5 KiB
Lua
-- 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)
|
||
|
||
-- 工具:1.1/1.2/1.3 新增能力巡检(媒体块 / 注入标志位 / 事件 / 动态通道注销)
|
||
-- 注意:故意不在这里调用 sdk.inject_input_sync——工具handler 运行在 LLM 回合内,
|
||
-- 同步注入会等本轮回复,等于自己等自己(死锁)。同步注入只适合事件回调等外部入口。
|
||
sdk.register_tool("luademo_probe_v2", {
|
||
description = "Exercise media blocks, inject opts, events and channel unregister",
|
||
parameters = { type = "object", properties = {} },
|
||
no_memory = true,
|
||
context_policy = "prune",
|
||
}, function(args)
|
||
local res = {}
|
||
|
||
-- 多模态:设置下一轮 tool message 携带的内容块
|
||
sdk.set_tool_blocks({
|
||
{ type = "text", text = "luademo media block" },
|
||
{ type = "image_url", image_url = { url = "https://example.com/x.png", detail = "low" } },
|
||
})
|
||
res.set_tool_blocks = "ok"
|
||
|
||
-- 注入标志位(零值 opts 与旧三参数等价)
|
||
sdk.inject_text_opts("luademo", "luademo_in", "opts inject", {
|
||
no_memory = true, context_policy = "prune",
|
||
})
|
||
res.inject_text_opts = "ok"
|
||
|
||
-- 带媒体的中断注入
|
||
sdk.inject_interrupt_media("luademo", "luademo_in", "media inject", {
|
||
{ type = "audio_url", audio_url = { url = "https://example.com/a.mp3" } },
|
||
})
|
||
res.inject_interrupt_media = "ok"
|
||
|
||
-- 媒体入记忆:三元组带原句,文档带附件
|
||
local _, merr = sdk.memory.commit({{
|
||
subject = "luademo", relation = "shows", object = "image",
|
||
sentence_text = "luademo shows an image", media_digests = {},
|
||
}})
|
||
res.memory_commit_with_sentence = { err = merr }
|
||
local _, derr = sdk.doc.insert_with_media(
|
||
{ id = "luademo-media", title = "media", content = "with attachment" },
|
||
{ { mime = "image/png", name = "x.png", data = "aGVsbG8=" } })
|
||
res.doc_insert_with_media = { err = derr }
|
||
|
||
-- 事件订阅(返回取消订阅函数)
|
||
local unsub = sdk.events.subscribe("agent_output", function(evt)
|
||
sdk.log("info", "luademo event: " .. tostring(evt.type))
|
||
end)
|
||
res.events_subscribe = type(unsub)
|
||
if unsub then unsub() end
|
||
|
||
-- 插件管理(只读查询)
|
||
res.plugin_mgr_loaded = type(sdk.plugin_mgr.list_loaded())
|
||
|
||
-- 动态输出通道注销
|
||
sdk.register_output_channel("luademo_dyn", 0, "dynamic", {}, function(a) return { ok = true } end)
|
||
local _, uerr = sdk.unregister_output_channel("luademo_dyn")
|
||
res.unregister = { err = uerr }
|
||
|
||
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")
|
||
|
||
-- 阶段钩子:全局作用域(修改 ctx 字段会写回内核,见 applyLuaStageResult)
|
||
sdk.register_stage("pre_action", function(ctx)
|
||
sdk.log("info", "luademo stage pre_action: user=" .. tostring(ctx.user_id))
|
||
-- 演示 stage 写回:给 llm_text 追加标记(内核会同步回 StageContext)
|
||
if ctx.llm_text then
|
||
ctx.llm_text = ctx.llm_text .. "[luademo]"
|
||
end
|
||
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
|