package majordomo import ( "context" "encoding/json" "strings" "testing" "gitea.stevedudenhoeffer.com/steve/majordomo/llm" "gitea.stevedudenhoeffer.com/steve/majordomo/provider/fake" ) type verdict struct { Guilty bool `json:"guilty"` Why string `json:"why"` } func TestGenerateTyped(t *testing.T) { r := newTestRegistry(t) fp := fake.New("fp") r.RegisterProvider(fp) fp.Enqueue("judge", fake.Reply(`{"guilty":true,"why":"caught red-handed"}`)) m, _ := r.Parse("fp/judge") v, err := Generate[verdict](context.Background(), m, Request{Messages: []Message{UserText("verdict?")}}) if err != nil { t.Fatalf("Generate: %v", err) } if !v.Guilty || v.Why != "caught red-handed" { t.Errorf("verdict = %+v", v) } // The schema derived from the struct reached the provider, named after // the type. req := fp.Calls()[0].Request if req.SchemaName != "verdict" { t.Errorf("schema name = %q", req.SchemaName) } var schema map[string]any if err := json.Unmarshal(req.Schema, &schema); err != nil { t.Fatalf("schema: %v", err) } props := schema["properties"].(map[string]any) if _, ok := props["guilty"]; !ok { t.Errorf("schema = %v", schema) } } func TestGenerateStripsMarkdownFence(t *testing.T) { r := newTestRegistry(t) fp := fake.New("fp") r.RegisterProvider(fp) fp.Enqueue("judge", fake.Reply("```json\n{\"guilty\":false,\"why\":\"alibi\"}\n```")) m, _ := r.Parse("fp/judge") v, err := Generate[verdict](context.Background(), m, Request{Messages: []Message{UserText("verdict?")}}) if err != nil { t.Fatalf("Generate: %v", err) } if v.Guilty || v.Why != "alibi" { t.Errorf("verdict = %+v", v) } } func TestGenerateDecodeErrorNamesModel(t *testing.T) { r := newTestRegistry(t) fp := fake.New("fp") r.RegisterProvider(fp) fp.Enqueue("judge", fake.Reply("not json at all")) m, _ := r.Parse("fp/judge") _, err := Generate[verdict](context.Background(), m, Request{Messages: []Message{UserText("verdict?")}}) if err == nil || !strings.Contains(err.Error(), "fp/judge") { t.Errorf("err = %v, want decode error naming the model", err) } } func TestGenerateEmptyResponse(t *testing.T) { r := newTestRegistry(t) fp := fake.New("fp") r.RegisterProvider(fp) fp.Enqueue("judge", fake.ReplyWith(llm.Response{FinishReason: llm.FinishLength})) m, _ := r.Parse("fp/judge") _, err := Generate[verdict](context.Background(), m, Request{Messages: []Message{UserText("verdict?")}}) if err == nil || !strings.Contains(err.Error(), "empty") { t.Errorf("err = %v, want empty-response error", err) } }