package main import ( "testing" "time" "git.cin.su/m/Assistant/contracts/capability" interactiondomain "git.cin.su/m/Assistant/modules/interaction/domain" ) func TestHouseholdResearchAndActionRoutesRemainIndependent(t *testing.T) { candidate := interactiondomain.BuildDeterministicCandidate("проверь погоду в Казани, найди кафе рядом и напомни вечером позвонить", "", time.Unix(10, 0).UnixMilli()) planned := buildChatRoutePlan(interactiondomain.SemanticResult{NeedsCognition: candidate.NeedsCognition, CapabilityHints: candidate.CapabilityHints}) want := map[chatRouteKind]bool{chatRouteResearch: false, chatRouteConversation: false, chatRouteAction: false} for _, route := range planned { if _, ok := want[route.Kind]; ok { want[route.Kind] = true } } for kind, present := range want { if !present { t.Fatalf("route %s missing for mixed household request: candidate=%+v routes=%+v", kind, candidate, planned) } } } func TestHouseholdCreativeRouteDoesNotUseResearch(t *testing.T) { candidate := interactiondomain.BuildDeterministicCandidate("расскажи сказку о Колобке", "", time.Unix(10, 0).UnixMilli()) for _, hint := range candidate.CapabilityHints { if capability.CapabilityKind(hint) == capability.CapabilityKindWebResearch { t.Fatalf("creative request selected web research: %+v", candidate) } } if !interactiondomain.IsCreativeRequest("расскажи сказку о Колобке") { t.Fatal("creative request was not recognized") } } func TestHouseholdMarketplaceBrandSearchUsesResearchRoute(t *testing.T) { candidate := interactiondomain.BuildDeterministicCandidate("найди на WB бренд marka-wear", "", time.Unix(10, 0).UnixMilli()) decomposition := interactiondomain.DecomposeTaskWithLexicon("найди на WB бренд marka-wear", interactiondomain.TaskLexicon{ObjectAliases: []interactiondomain.TaskLexiconEntry{{Alias: "бренд", Value: "BRAND"}}, SourceAliases: []interactiondomain.TaskLexiconEntry{{Alias: "wb", Value: "WILDBERRIES"}}}) if decomposition.Intent != interactiondomain.TaskIntentSearch || decomposition.Object != "BRAND" || decomposition.Source != "WILDBERRIES" || decomposition.Target != "marka-wear" || len(decomposition.Missing) != 0 { t.Fatalf("marketplace request was not split into executable atoms: %+v", decomposition) } routes := buildChatRoutePlan(interactiondomain.SemanticResult{NeedsCognition: candidate.NeedsCognition, CapabilityHints: candidate.CapabilityHints}) if !hasChatRoute(routes, chatRouteResearch) { t.Fatalf("marketplace brand request did not select research route: candidate=%+v routes=%+v", candidate, routes) } } func TestHouseholdSemanticMemoryControlsMarketplaceAlias(t *testing.T) { withoutMemory := interactiondomain.DecomposeTask("найди на WB бренд marka-wear") if withoutMemory.Source != "" || withoutMemory.Object != "" { t.Fatalf("core invented marketplace meaning without memory: %+v", withoutMemory) } withMemory := interactiondomain.DecomposeTaskWithLexicon("поищи марку marka-wear в Wildberries", interactiondomain.TaskLexicon{ ObjectAliases: []interactiondomain.TaskLexiconEntry{{Alias: "марку", Value: "BRAND"}}, SourceAliases: []interactiondomain.TaskLexiconEntry{{Alias: "wildberries", Value: "WILDBERRIES"}}, }) if withMemory.Intent != interactiondomain.TaskIntentSearch || withMemory.Object != "BRAND" || withMemory.Source != "WILDBERRIES" || withMemory.Target != "marka-wear" || !withMemory.NeedsResearch { t.Fatalf("semantic memory did not control marketplace decomposition: %+v", withMemory) } } func TestHouseholdBearDrawingUsesCreativeLocalRoute(t *testing.T) { candidate := interactiondomain.BuildDeterministicCandidate("нарисуй бурого медведя", "", time.Unix(10, 0).UnixMilli()) decomposition := interactiondomain.DecomposeTaskWithLexicon("нарисуй бурого медведя", interactiondomain.TaskLexicon{ObjectAliases: []interactiondomain.TaskLexiconEntry{{Alias: "медвед", Value: "IMAGE"}}}) if decomposition.Intent != interactiondomain.TaskIntentCreate || decomposition.Object != "IMAGE" || decomposition.Target != "бурого медведя" || decomposition.NeedsResearch { t.Fatalf("creative request was not split into local execution atoms: %+v", decomposition) } routes := buildChatRoutePlan(interactiondomain.SemanticResult{NeedsCognition: candidate.NeedsCognition, CapabilityHints: candidate.CapabilityHints}) if !hasChatRoute(routes, chatRouteMedia) { t.Fatalf("creative image request did not select media route: candidate=%+v routes=%+v", candidate, routes) } for _, hint := range candidate.CapabilityHints { if capability.CapabilityKind(hint) == capability.CapabilityKindWebResearch { t.Fatalf("bear drawing selected web research: %+v", candidate) } } for _, message := range []string{"нарисуй бурого медведя", "нарисуй птицу", "нарисуй зеленую птицу", "нарисуй птицу в HD", "нарисуй коня"} { if got := interactiondomain.CreativeDeterministicReply(message); got != "" { t.Fatalf("creative request %q received a canned reply=%q", message, got) } } } func TestHouseholdRouteMatrixKeepsMeaningBoundaries(t *testing.T) { tests := []struct { message string wantResearch bool wantCreative bool wantHint string }{ {message: "найди на WB бренд marka-wear", wantResearch: true}, {message: "какая погода в Казани?", wantResearch: true}, {message: "найди билеты до Сочи", wantResearch: true}, {message: "нарисуй птицу", wantCreative: true}, {message: "расскажи сказку о Колобке", wantCreative: true}, {message: "что такое гравитация?", wantHint: "COGNITIVE_REASONING"}, } for _, test := range tests { candidate := interactiondomain.BuildDeterministicCandidate(test.message, "", time.Unix(10, 0).UnixMilli()) if interactiondomain.IsCreativeRequest(test.message) != test.wantCreative { t.Fatalf("message=%q creative=%v want=%v candidate=%+v", test.message, interactiondomain.IsCreativeRequest(test.message), test.wantCreative, candidate) } gotResearch := false gotHint := false for _, hint := range candidate.CapabilityHints { if hint == "WEB_RESEARCH" { gotResearch = true } if hint == test.wantHint { gotHint = true } } if gotResearch != test.wantResearch { t.Fatalf("message=%q research=%v want=%v candidate=%+v", test.message, gotResearch, test.wantResearch, candidate) } if test.wantHint != "" && !gotHint { t.Fatalf("message=%q missing hint=%q candidate=%+v", test.message, test.wantHint, candidate) } } }