mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 09:28:14 +00:00
fix(cabi): applyStageResult 支持 diff 回传(plan 11.3 配套)
外部插件 bridge 模板改为只回传变更字段后(SDK 仓 5648519),内核侧配套: - applyStageResult 的 tool_calls/tool_results 去掉 len(v)>0 拦截——改为键存在即应用, 使插件「清空全部工具调用」的显式回传 [] 能被表达(旧插件仅 len>0 才带键,不误清空) - 逐字段应用,未回传的键保持原值(diff 语义:只改变更字段,不覆盖他人改写) - output_test.go 新增 TestApplyStageResult_ClearedSlicesAreApplied / _OnlyPresentKeysApplied 验证: go build exit 0; go test ./internal/plugin/... ./internal/agent/... 全绿
This commit is contained in:
@ -360,7 +360,10 @@ func applyStageResult(sc *sdk.StageContext, resultJSON string) {
|
||||
vv := v
|
||||
sc.Response = &vv
|
||||
}
|
||||
if v, ok := m["tool_calls"].([]interface{}); ok && len(v) > 0 {
|
||||
if v, ok := m["tool_calls"].([]interface{}); ok {
|
||||
// 注意不要加 len(v)>0 条件:ABI v2 diff 回传(plan.md 11.3)下,插件拒绝全部
|
||||
// 工具调用时会显式回传 `[]`,必须能表达「清空」。旧插件(全量回传)仅在
|
||||
// len>0 时才带该键,因此不会因此变更而被误清空。
|
||||
if b, err := json.Marshal(v); err == nil {
|
||||
var tcs []sdk.ToolCall
|
||||
if json.Unmarshal(b, &tcs) == nil {
|
||||
@ -368,7 +371,7 @@ func applyStageResult(sc *sdk.StageContext, resultJSON string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if v, ok := m["tool_results"].([]interface{}); ok && len(v) > 0 {
|
||||
if v, ok := m["tool_results"].([]interface{}); ok {
|
||||
if b, err := json.Marshal(v); err == nil {
|
||||
var trs []sdk.ToolResult
|
||||
if json.Unmarshal(b, &trs) == nil {
|
||||
|
||||
@ -5,9 +5,11 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||||
)
|
||||
|
||||
// awaitOutputResult 的 decision 核心:
|
||||
// output_send 不再假成功(plan.md 11.1):sent / error / unconfirmed 三态。
|
||||
|
||||
func TestAwaitOutputResult_Success(t *testing.T) {
|
||||
res, err := awaitOutputResultWith(0, "qq", `{"x":1}`, func(pid int32, ch, args string) error {
|
||||
@ -47,3 +49,44 @@ func TestAwaitOutputResult_Timeout(t *testing.T) {
|
||||
t.Fatalf("expected status=unconfirmed, got %v", m["status"])
|
||||
}
|
||||
}
|
||||
|
||||
// applyStageResult 必须能表达「插件清空了 tool_calls/tool_results」——
|
||||
// ABI v2 diff 回传(plan.md 11.3)下插件拒绝全部工具调用时会显式回传 []。
|
||||
func TestApplyStageResult_ClearedSlicesAreApplied(t *testing.T) {
|
||||
sc := &sdk.StageContext{
|
||||
ToolCalls: []sdk.ToolCall{{ID: "t1", Name: "cmd_run"}},
|
||||
ToolResults: []sdk.ToolResult{{CallID: "t1", Name: "cmd_run", Result: "x"}},
|
||||
}
|
||||
applyStageResult(sc, `{"tool_calls":[],"tool_results":[]}`)
|
||||
if len(sc.ToolCalls) != 0 {
|
||||
t.Fatalf("tool_calls 应被清空,实际 %v", sc.ToolCalls)
|
||||
}
|
||||
if len(sc.ToolResults) != 0 {
|
||||
t.Fatalf("tool_results 应被清空,实际 %v", sc.ToolResults)
|
||||
}
|
||||
}
|
||||
|
||||
// diff 回传只带变更字段:未出现的键不得被改动(避免旧快照覆盖)。
|
||||
func TestApplyStageResult_OnlyPresentKeysApplied(t *testing.T) {
|
||||
sc := &sdk.StageContext{
|
||||
RawMessage: "原始输入",
|
||||
LLMText: "原始LLM",
|
||||
FinalText: "原始最终",
|
||||
ToolResults: []sdk.ToolResult{{CallID: "c1", Result: "已清洗"}},
|
||||
}
|
||||
// 只回传 final_text 的变更
|
||||
applyStageResult(sc, `{"final_text":"新最终"}`)
|
||||
|
||||
if sc.FinalText != "新最终" {
|
||||
t.Fatalf("final_text 应被应用,实际 %q", sc.FinalText)
|
||||
}
|
||||
if sc.RawMessage != "原始输入" {
|
||||
t.Errorf("raw_message 未回传却被改动: %q", sc.RawMessage)
|
||||
}
|
||||
if sc.LLMText != "原始LLM" {
|
||||
t.Errorf("llm_text 未回传却被改动: %q", sc.LLMText)
|
||||
}
|
||||
if len(sc.ToolResults) != 1 || sc.ToolResults[0].Result != "已清洗" {
|
||||
t.Errorf("tool_results 未回传却被改动: %v", sc.ToolResults)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user