From ab27d1a7c3630b7488249b64090398151f61c0a7 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sun, 25 Oct 2026 10:08:00 +0800 Subject: [PATCH] =?UTF-8?q?chore(sdk):=20=E5=90=8C=E6=AD=A5=20SDK=20?= =?UTF-8?q?=E4=BB=93=E5=89=AF=E6=9C=AC=20=E2=80=94=E2=80=94=20=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E5=8F=96=E6=B6=88=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对应 SDK 仓提交 ceb3bee(feature/cancel-and-interop-fixes 合回 main)。 本次同步只含 sdk/plugin.go 的公开接口新增(ToolHandlerCtx / RegisterToolCtx / SetToolRegistrarCtx),符合 §八「只增不减、签名不改」: · 纯新增,无任何删除行(git diff 验证:无 "-" 行) · 既有 ToolHandler / RegisterTool 签名与行为完全不变 · 用旧接口注册的工具照旧运行,只是拿不到取消信号 A2A/ACP 两个 example 与 hmapdev 模板的改动不在本仓同步范围 (它们由 SDK 仓独立管理,本仓不跟踪 example/ 与 tools/)。 Co-Authored-By: Claude --- third_party/homeagent-sdk/sdk/plugin.go | 66 +++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/third_party/homeagent-sdk/sdk/plugin.go b/third_party/homeagent-sdk/sdk/plugin.go index e2919ef1..8bf295c8 100644 --- a/third_party/homeagent-sdk/sdk/plugin.go +++ b/third_party/homeagent-sdk/sdk/plugin.go @@ -1,6 +1,7 @@ package sdk import ( + "context" "sync" "github.com/JianFeeeee/hologramagentsdk/meta" @@ -19,6 +20,39 @@ type Plugin interface { // ToolHandler is a function that handles a tool call. type ToolHandler func(args map[string]interface{}) (interface{}, error) +// ToolHandlerCtx 是**可选**的带取消语义 handler。 +// +// ## 插件作者**不需要**改代码 +// +// 内核超时后要让插件停止执行,这件事由**运行时**接住:proc 模板收到 +// call.cancel 就 cancel 对应调用的 ctx。用旧签名的 handler 照旧跑完, +// 行为与今天完全一致;只有**主动想配合取消**的长任务工具才需要这个类型。 +// +// ## 什么时候值得用 +// +// 执行体里有长阻塞(HTTP、等子进程、轮询设备)时,用 Ctx 版可以让取消 +// 立即生效,而不是白跑完再丢结果。纯内存计算(毫秒级)没有必要。 +// +// ## 约定 +// +// - ctx 只在**本次调用**内有效,不要缓存; +// - 把 ctx 传给下游阻塞调用(http.NewRequestWithContext / exec.CommandContext); +// - 返回时用 defer 清理;不要假设调用方还在等结果(它可能已经走了)。 +type ToolHandlerCtx func(ctx context.Context, args map[string]interface{}) (interface{}, error) + +// AsToolHandler 把带 ctx 的 handler 适配成旧签名(无取消信号)。 +func AsToolHandler(h ToolHandlerCtx) ToolHandler { + if h == nil { + return nil + } + return func(args map[string]interface{}) (interface{}, error) { + return h(context.Background(), args) + } +} + +// ToolRegistrarCtx 是带 ctx 的注册口(由运行时/宿主注入)。 +type ToolRegistrarCtx func(name string, def ToolDef, handler ToolHandlerCtx) error + // StageHandler is a function that handles a pipeline stage event. type StageHandler func(ctx *StageContext) error @@ -512,6 +546,7 @@ const ( type PluginSDK struct { name string regTool ToolRegistrar + regToolCtx ToolRegistrarCtx regStage StageRegistrar regAPI APIRegistrar regOutput OutputChannelRegistrar @@ -642,6 +677,37 @@ func (s *PluginSDK) Events() EventSubscriber { return s.events } +// SetToolRegistrarCtx 由**运行时**注入带 ctx 的注册口。 +// +// 插件业务代码不需要调用它:proc 模板在自己的初始化里注入, +// 之后插件用 RegisterToolCtx 注册的工具就会进入可取消路径。 +func (s *PluginSDK) SetToolRegistrarCtx(r ToolRegistrarCtx) { + s.apiMu.Lock() + s.regToolCtx = r + s.apiMu.Unlock() +} + +// RegisterToolCtx 注册一个**可取消**的工具(可选,长任务推荐)。 +// +// 宿主不支持时自动降级到 RegisterTool 并包一层 context.Background(): +// 功能不丢,只是拿不到取消。插件不必为此写两套代码。 +func (s *PluginSDK) RegisterToolCtx(name string, def ToolDef, handler ToolHandlerCtx) error { + if def.Plugin == "" { + def.Plugin = s.name + } + s.apiMu.RLock() + regCtx, regLegacy := s.regToolCtx, s.regTool + s.apiMu.RUnlock() + + if regCtx != nil { + return regCtx(name, def, handler) + } + if regLegacy != nil { + return regLegacy(name, def, AsToolHandler(handler)) + } + return nil +} + // RegisterTool registers a tool that the LLM can call. func (s *PluginSDK) RegisterTool(name string, def ToolDef, handler ToolHandler) error { if def.Plugin == "" {