Files
Sakurasan d5dac5a6fc 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。
2026-10-26 00:54:46 +08:00

93 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}
}