package loop import ( "context" "errors" "fmt" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model" ) // fakeSourceErr 模拟 vision 包的 SourceFailure:错误链上能报出来源主机。 type fakeSourceErr struct{ host string } func (e *fakeSourceErr) Error() string { return "取回失败" } func (e *fakeSourceErr) ImageSourceHost() string { return e.host } // TestImageSourceHost 钉住「日志能定位到来源主机」这条契约:错误链上 // 带 ImageSourceHost 的证据要穿透 %w 包装被取出;其它错误一律空串 // (空串表示无从定位,而不是假装有来源)。 func TestImageSourceHost(t *testing.T) { t.Parallel() cases := []struct { name string err error want string }{ {name: "nil 错误", err: nil, want: ""}, {name: "普通错误", err: errors.New("解析失败"), want: ""}, {name: "包装后的来源错误", err: fmt.Errorf("resolve: %w", &fakeSourceErr{host: "cdn.example.com"}), want: "cdn.example.com"}, {name: "包装但主机为空", err: fmt.Errorf("resolve: %w", &fakeSourceErr{}), want: ""}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { t.Parallel() if got := imageSourceHost(c.err); got != c.want { t.Fatalf("imageSourceHost = %q, want %q", got, c.want) } }) } } func TestRun_ImageResolverFoldsToText(t *testing.T) { t.Parallel() ad := &mockAdapter{chat: []mgResp{{resp: model.Response{Model: "m", Content: "Done."}}}} a := newTestAgent(t, ad, nil) l := New(Options{ Agent: a, MaxRounds: 8, Vision: ImageResolveFunc(func(_ context.Context, _ model.Message) (model.Message, error) { return model.TextMessage(model.RoleUser, "图中是一只猫"), nil }), }) in := []model.Message{model.ImageMessage(model.RoleUser, "", "data:image/png;base64,AAAA")} if _, err := l.Run(context.Background(), "sess-1", in); err != nil { t.Fatalf("Run: %v", err) } if len(ad.reqs) == 0 { t.Fatalf("no model request recorded") } var userSeen bool for _, m := range ad.reqs[0].Messages { if m.Role == model.RoleUser { userSeen = true if messageHasImage(m) { t.Fatalf("image still reached the model: %+v", m) } } } if !userSeen { t.Fatalf("no user message seen by the model") } } func TestRun_ImageKeptWithoutResolver(t *testing.T) { t.Parallel() ad := &mockAdapter{chat: []mgResp{{resp: model.Response{Model: "m", Content: "Done."}}}} a := newTestAgent(t, ad, nil) l := New(Options{Agent: a, MaxRounds: 8}) in := []model.Message{model.ImageMessage(model.RoleUser, "look", "data:image/png;base64,AAAA")} if _, err := l.Run(context.Background(), "sess-1", in); err != nil { t.Fatalf("Run: %v", err) } if len(ad.reqs) == 0 { t.Fatalf("no model request recorded") } var sawImage bool for _, m := range ad.reqs[0].Messages { if m.Role == model.RoleUser && messageHasImage(m) { sawImage = true } } if !sawImage { t.Fatalf("expected the image part to pass through without a resolver") } }