Files
Assistant/modules/interaction/domain/chat_test.go
m b097269df4
CI / test (push) Successful in 23s
CI / test (pull_request) Successful in 21s
Route local recovery by semantic intent
2026-08-22 03:00:15 +03:00

140 lines
6.2 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 domain
import (
"testing"
"time"
"git.cin.su/m/Assistant/contracts/identity"
"git.cin.su/m/Assistant/contracts/interaction"
)
func TestNewHumanUtteranceClassifiesQuestionAndRequiresCognition(t *testing.T) {
conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen}
utterance, intent, relations, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: "Кто ты?"}, time.Unix(10, 0))
if err != nil {
t.Fatal(err)
}
if utterance.SpeechAct != interaction.SpeechActQuestion || !NeedsCognition(utterance.SpeechAct) {
t.Fatalf("expected cognitive question, got %s", utterance.SpeechAct)
}
if intent.Text != "Кто ты?" || len(relations) != 0 {
t.Fatalf("unexpected intent or relations")
}
}
func TestNewHumanUtteranceAppendsCorrectionRelation(t *testing.T) {
conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen}
utterance, _, relations, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: "исправь прошлое", CorrectsUtteranceID: "utterance-0"}, time.Unix(10, 0))
if err != nil {
t.Fatal(err)
}
if utterance.SpeechAct != interaction.SpeechActCorrection {
t.Fatalf("expected correction, got %s", utterance.SpeechAct)
}
if len(relations) != 1 || relations[0].Kind != interaction.RelationCorrects || relations[0].ToUtteranceID != "utterance-0" {
t.Fatalf("unexpected correction relation: %#v", relations)
}
}
func TestNewHumanUtteranceClassifiesReminderAndCapabilityConnectionAsDirective(t *testing.T) {
conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen}
for _, message := range []string{"Напомни завтра позвонить врачу.", "Подключи интеграцию с неизвестным сервисом."} {
utterance, _, _, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: message}, time.Unix(10, 0))
if err != nil {
t.Fatal(err)
}
if utterance.SpeechAct != interaction.SpeechActDirective {
t.Fatalf("expected directive for %q, got %s", message, utterance.SpeechAct)
}
}
}
func TestDeterministicCandidateIsInvariantToMissingQuestionMarkForIdentityAndArithmetic(t *testing.T) {
cases := []struct {
message string
act interaction.SpeechAct
}{
{"ты кто", interaction.SpeechActQuestion},
{"Ты кто?", interaction.SpeechActQuestion},
{"а ты кто", interaction.SpeechActQuestion},
{"сколько будет 2 + 2", interaction.SpeechActQuestion},
{"сколько будет 2 + 2?", interaction.SpeechActQuestion},
{"2 плюс 2 сколько", interaction.SpeechActQuestion},
}
for _, tc := range cases {
candidate := BuildDeterministicCandidate(tc.message, "", time.Unix(10, 0).UnixMilli())
if candidate.PrimarySpeechAct != tc.act {
t.Fatalf("expected %s for %q, got %s (%+v)", tc.act, tc.message, candidate.PrimarySpeechAct, candidate)
}
if candidate.Applicability == interaction.SemanticApplicable && candidate.NeedsCognition {
continue
}
if !candidate.NeedsCognition {
t.Fatalf("expected cognition-worthy candidate for %q, got %+v", tc.message, candidate)
}
}
}
func TestDeterministicCandidateTreatsModalExplanationRequestsAsCognitionWorthWithoutQuestionMark(t *testing.T) {
for _, message := range []string{
"можешь рассказать об утре",
"можешь расказать об утре",
"можете объяснить это проще",
"расскажи об утре",
"про утро расскажешь",
} {
candidate := BuildDeterministicCandidate(message, "", time.Unix(10, 0).UnixMilli())
if candidate.PrimarySpeechAct != interaction.SpeechActQuestion && candidate.PrimarySpeechAct != interaction.SpeechActRequestToInvestigate {
t.Fatalf("expected broader cognitive speech act for %q, got %+v", message, candidate)
}
if !candidate.NeedsCognition {
t.Fatalf("expected cognition for %q, got %+v", message, candidate)
}
}
}
func TestDeterministicCandidateTreatsModelRequestAsDirectiveNeedingCognition(t *testing.T) {
candidate := BuildDeterministicCandidate("обратись к модели gemma4 и спроси сколько звезд на небе", "", time.Unix(10, 0).UnixMilli())
if candidate.PrimarySpeechAct != interaction.SpeechActDirective {
t.Fatalf("expected directive, got %+v", candidate)
}
if !candidate.NeedsCognition {
t.Fatalf("expected cognition-worthy model request, got %+v", candidate)
}
}
func TestInformationRequestsAreClassifiedOnceByInteraction(t *testing.T) {
cases := []struct {
message string
kind interaction.InformationRequestKind
}{
{"какие модели доступны?", interaction.InformationRequestCognitiveModelInventory},
{"что ты умеешь?", interaction.InformationRequestCapabilityInventory},
{"проверь состояние локального канала", interaction.InformationRequestCognitiveChannelStatus},
{"нет связи с ИИ, восстанови канал", interaction.InformationRequestCognitiveRecovery},
}
for _, test := range cases {
result, err := InterpretUtterance(interaction.HumanUtterance{Content: test.message}, nil, nil, time.Unix(10, 0))
if err != nil {
t.Fatalf("interpret %q: %v", test.message, err)
}
if result.InformationRequestKind != test.kind {
t.Fatalf("message=%q kind=%q want=%q", test.message, result.InformationRequestKind, test.kind)
}
}
}
func TestDeterministicCandidateKeepsFirstPersonCompletedWorkAsAssertion(t *testing.T) {
candidate := BuildDeterministicCandidate("Я всё уже настроил.", "", time.Unix(10, 0).UnixMilli())
if candidate.PrimarySpeechAct != interaction.SpeechActAssertion {
t.Fatalf("expected assertion, got %+v", candidate)
}
}
func TestNewConversationRequiresActorAndPrincipal(t *testing.T) {
_, err := NewConversation(identity.ActorID(""), identity.PrincipalID("principal-1"), time.Unix(10, 0))
if err == nil {
t.Fatal("expected invalid principal")
}
}