mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
外部插件 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/... 全绿
93 lines
3.0 KiB
Go
93 lines
3.0 KiB
Go
package cabi
|
||
|
||
import (
|
||
"errors"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||
)
|
||
|
||
// 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 {
|
||
return nil
|
||
}, outputSendTimeout)
|
||
if err != nil {
|
||
t.Fatalf("expected no error, got %v", err)
|
||
}
|
||
m, _ := res.(map[string]interface{})
|
||
if m["status"] != "sent" {
|
||
t.Fatalf("expected status=sent, got %v", m["status"])
|
||
}
|
||
}
|
||
|
||
func TestAwaitOutputResult_Failure(t *testing.T) {
|
||
_, err := awaitOutputResultWith(0, "qq", `{}`, func(pid int32, ch, args string) error {
|
||
return errors.New("meta 中需要 group_id 或 user_id 字段")
|
||
}, outputSendTimeout)
|
||
if err == nil {
|
||
t.Fatal("expected error on failed send, got nil (旧实现会谎报成功)")
|
||
}
|
||
if !strings.Contains(err.Error(), "需要 group_id") {
|
||
t.Fatalf("unexpected error: %v", err)
|
||
}
|
||
}
|
||
|
||
func TestAwaitOutputResult_Timeout(t *testing.T) {
|
||
res, err := awaitOutputResultWith(0, "qq", `{}`, func(pid int32, ch, args string) error {
|
||
time.Sleep(2 * time.Second) // 模拟插件发送迟迟不确认
|
||
return nil
|
||
}, 50*time.Millisecond)
|
||
if err != nil {
|
||
t.Fatalf("unconfirmed 不应返回 error,got %v", err)
|
||
}
|
||
m, _ := res.(map[string]interface{})
|
||
if m["status"] != "unconfirmed" {
|
||
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)
|
||
}
|
||
}
|