feat: AutoZero 通用 Agent 框架(从 AutoGo 五阶段拆分完成)

llm(三协议 Provider)/ session(JSONL 持久化)/ tools(Registry、围栏、
builtin、computer)/ cdp(browser)/ skills / mcp / agent(循环、两级压缩、
编排、审批门控)+ 底座 jsonl/textx/execx/diag/scratch/spill/imagecache。

产品装配代码(config 桥接、schedule 工具、memory/plan/retro/prompt)
留在 AutoGo,宿主接入方式见 MIGRATION.md。
This commit is contained in:
Sakurasan committed 2026-10-26 00:54:46 +08:00
commit d5dac5a6fc
140 files changed
+26497

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
package llm
import (
"encoding/base64"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
// 1x1 红色 PNG
const tinyPNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
func writeTinyPNG(t *testing.T) string {
t.Helper()
data, _ := base64.StdEncoding.DecodeString(tinyPNG)
path := filepath.Join(t.TempDir(), "shot.png")
if err := os.WriteFile(path, data, 0o644); err != nil {
t.Fatal(err)
}
return path
}
func TestLoadImage(t *testing.T) {
path := writeTinyPNG(t)
b64, mt, err := LoadImage(path)
if err != nil {
t.Fatal(err)
}
if mt != "image/png" || b64 != tinyPNG {
t.Fatalf("roundtrip failed: mt=%s len=%d", mt, len(b64))
}
if _, _, err := LoadImage("x.txt"); err == nil {
t.Fatal("expected unsupported type error")
}
}
func TestOpenAISerializationWithImages(t *testing.T) {
path := writeTinyPNG(t)
msgs := []Message{
{Role: RoleUser, Content: "look", Images: []Image{{Path: path}}},
{Role: RoleTool, ToolCallID: "t1", Content: "done", Images: []Image{{Path: path}}},
}
out := oaBuildMessages("sys", msgs)
// user 消息 → content parts 数组(text + image_url)
u := out[1]
if u["role"] != "user" {
t.Fatalf("role: %v", u["role"])
}
parts, _ := json.Marshal(u["content"])
if !strings.Contains(string(parts), `"image_url"`) || !strings.Contains(string(parts), "data:image/png;base64,") {
t.Fatalf("user image parts: %s", parts)
}
// tool result → tool 消息 + 紧随的 user 附图消息
if out[2]["role"] != "tool" || out[3]["role"] != "user" {
t.Fatalf("tool+image layout wrong: %v %v", out[2]["role"], out[3]["role"])
}
parts, _ = json.Marshal(out[3]["content"])
if !strings.Contains(string(parts), "data:image/png") {
t.Fatalf("tool image parts: %s", parts)
}
}
func TestAnthropicSerializationWithImages(t *testing.T) {
path := writeTinyPNG(t)
msgs := []Message{
{Role: RoleTool, ToolCallID: "t1", Content: "shot", Images: []Image{{Path: path}}},
{Role: RoleUser, Content: "look again", Images: []Image{{Path: path}}},
}
out := anBuildMessages(msgs, false)
body, _ := json.Marshal(out)
if !strings.Contains(string(body), `"type":"tool_result"`) ||
!strings.Contains(string(body), `"type":"image"`) ||
!strings.Contains(string(body), `"media_type":"image/png"`) {
t.Fatalf("anthropic serialization: %s", body)
}
}
func TestImageUnavailableDegradation(t *testing.T) {
msgs := []Message{{Role: RoleUser, Content: "look", Images: []Image{{Path: "/nonexistent/shot.png"}}}}
oa := oaBuildMessages("s", msgs)
body, _ := json.Marshal(oa[1]["content"])
if !strings.Contains(string(body), "[image unavailable") {
t.Fatalf("openai degradation missing: %s", body)
}
an := anBuildMessages(msgs, false)
body, _ = json.Marshal(an[0]["content"])
if !strings.Contains(string(body), "[image unavailable") {
t.Fatalf("anthropic degradation missing: %s", body)
}
}