404 lines
21 KiB
Go
404 lines
21 KiB
Go
package domain
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"git.cin.su/m/Assistant/contracts/identity"
|
||
"git.cin.su/m/Assistant/contracts/interaction"
|
||
"git.cin.su/m/Assistant/contracts/situation"
|
||
"git.cin.su/m/Assistant/platform/ids"
|
||
)
|
||
|
||
func TestInterpretUtteranceRecordsExplicitProjectFocus(t *testing.T) {
|
||
result := interpretForTest(t, "Сейчас работаем с УПО.", nil, "")
|
||
if result.ActiveFocus == nil || result.ActiveFocus.ProjectID != "upo" {
|
||
t.Fatalf("expected explicit UPO focus, got %#v", result.ActiveFocus)
|
||
}
|
||
if result.Interpretation.SpeechAct != interaction.SpeechActAssertion {
|
||
t.Fatalf("expected assertion, got %s", result.Interpretation.SpeechAct)
|
||
}
|
||
if result.Projects[0].Provenance != situation.ProjectResolutionExplicit {
|
||
t.Fatalf("expected explicit provenance, got %#v", result.Projects)
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceAssertionIsNotVerifiedFact(t *testing.T) {
|
||
result := interpretForTest(t, "На сервере сейчас свободно 300 ГБ.", nil, "")
|
||
if result.Interpretation.SpeechAct != interaction.SpeechActAssertion {
|
||
t.Fatalf("expected assertion, got %s", result.Interpretation.SpeechAct)
|
||
}
|
||
if result.Interpretation.AssertionVerification != "UNVERIFIED" || !strings.Contains(result.Reply, "не проверенный факт") {
|
||
t.Fatalf("expected unverified human assertion, got %#v reply=%q", result.Interpretation, result.Reply)
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceDirectiveRecordsOutcomeWithoutExecution(t *testing.T) {
|
||
result := interpretForTest(t, "Поменяй базу на УПО1.", activeFocus("upo", "УПО"), "")
|
||
if result.Interpretation.SpeechAct != interaction.SpeechActDirective && result.Interpretation.SpeechAct != interaction.SpeechActRequestToInvestigate {
|
||
t.Fatalf("expected directive-style intent, got %s", result.Interpretation.SpeechAct)
|
||
}
|
||
if result.Interpretation.RequestedOutcome == "" || !strings.Contains(result.Reply, "Ничего не выполнено") {
|
||
t.Fatalf("expected non-executed requested outcome, got %#v reply=%q", result.Interpretation, result.Reply)
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceCorrectionIsAppendOnlySupersedes(t *testing.T) {
|
||
result := interpretForTest(t, "Нет, используй УПО2.", activeFocus("upo", "УПО"), "utterance-previous")
|
||
if result.Interpretation.SpeechAct != interaction.SpeechActCorrection {
|
||
t.Fatalf("expected correction, got %s", result.Interpretation.SpeechAct)
|
||
}
|
||
if len(result.Relations) != 1 || result.Relations[0].Kind != interaction.RelationSupersedes || result.Relations[0].ToUtteranceID != "utterance-previous" {
|
||
t.Fatalf("expected supersedes relation, got %#v", result.Relations)
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceQuestionAboutCurrentWorkUsesFocus(t *testing.T) {
|
||
result := interpretForTest(t, "Что мы сейчас делаем?", activeFocus("assistant", "Assistant"), "")
|
||
if result.Interpretation.SpeechAct != interaction.SpeechActQuestion {
|
||
t.Fatalf("expected question, got %s", result.Interpretation.SpeechAct)
|
||
}
|
||
if !strings.Contains(result.Reply, "Assistant") {
|
||
t.Fatalf("expected focus-aware reply, got %q", result.Reply)
|
||
}
|
||
if result.NeedsCognition {
|
||
t.Fatalf("focus question should be answered from canonical focus without cognition")
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceInheritedFocusForThere(t *testing.T) {
|
||
result := interpretForTest(t, "Добавь туда новую настройку.", activeFocus("assistant", "Assistant"), "")
|
||
if len(result.Projects) != 1 || result.Projects[0].ProjectID != "assistant" || result.Projects[0].Provenance != situation.ProjectResolutionInheritedFocus {
|
||
t.Fatalf("expected inherited Assistant focus, got %#v", result.Projects)
|
||
}
|
||
}
|
||
|
||
func TestProjectFocusIsContextualEvidenceNotGlobalMode(t *testing.T) {
|
||
explicit := interpretForTest(t, "Сейчас работаем с УПО.", nil, "")
|
||
if explicit.ActiveFocus == nil || explicit.ActiveFocus.ProjectID != "upo" {
|
||
t.Fatalf("expected explicit UPO focus, got %#v", explicit.ActiveFocus)
|
||
}
|
||
focusedQuestion := interpretForTest(t, "Почему тормозит база?", activeFocus("upo", "УПО"), "")
|
||
if len(focusedQuestion.Projects) != 1 || focusedQuestion.Projects[0].ProjectID != "upo" || focusedQuestion.Projects[0].Provenance != situation.ProjectResolutionInheritedFocus {
|
||
t.Fatalf("database follow-up can use previous project as contextual evidence, got %#v", focusedQuestion.Projects)
|
||
}
|
||
unrelated := interpretForTest(t, "Помоги ответить на письмо бухгалтеру.", activeFocus("upo", "УПО"), "")
|
||
if len(unrelated.Projects) != 0 {
|
||
t.Fatalf("unrelated mail intent must not inherit UPO focus, got %#v", unrelated.Projects)
|
||
}
|
||
returned := interpretForTest(t, "Вернемся к УПО.", activeFocus("assistant", "Assistant"), "")
|
||
if returned.ActiveFocus == nil || returned.ActiveFocus.ProjectID != "upo" {
|
||
t.Fatalf("expected explicit return to UPO, got %#v", returned.ActiveFocus)
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceMultiProjectReferenceRoles(t *testing.T) {
|
||
result := interpretForTest(t, "Посмотри, как это сделано в Assistant, и сделай аналогично в УПО.", nil, "")
|
||
if len(result.Projects) != 2 {
|
||
t.Fatalf("expected two project references, got %#v", result.Projects)
|
||
}
|
||
roles := map[situation.ProjectID]interaction.ProjectReferenceRole{}
|
||
for _, ref := range result.Projects {
|
||
roles[ref.ProjectID] = ref.Role
|
||
}
|
||
if roles["assistant"] != interaction.ProjectRoleReferenceSource || roles["upo"] != interaction.ProjectRolePrimary {
|
||
t.Fatalf("unexpected roles: %#v", roles)
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceAmbiguousDestructiveRequiresClarification(t *testing.T) {
|
||
result := interpretForTest(t, "Удали туда старую базу.", activeFocus("assistant", "Assistant"), "")
|
||
if result.Interpretation.Clarification != interaction.ClarificationRequired {
|
||
t.Fatalf("expected clarification, got %#v", result.Interpretation)
|
||
}
|
||
if result.NeedsCognition {
|
||
t.Fatalf("ambiguous destructive directive must not require cognition")
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceNaturalRussianHelpflowUsesContextAwareCognitionInput(t *testing.T) {
|
||
cases := []struct {
|
||
message string
|
||
wantSpeech interaction.SpeechAct
|
||
wantCognition bool
|
||
allowNoMatch bool
|
||
}{
|
||
{message: "расскажи как это работает", wantSpeech: interaction.SpeechActQuestion, wantCognition: true},
|
||
{message: "Расскажи, как это работает?", wantSpeech: interaction.SpeechActQuestion, wantCognition: true},
|
||
{message: "Расскажи как это работает", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "можешь объяснить почему база растёт", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "ПОЧЕМУ postgres использует так много памяти", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "ПОЧЕМУ postgres использует столько памяти", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "разберись почему сервис упал", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "разберись почему сервис упал", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "посмотри что произошло", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "исправь ошибку если найдёшь", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "разберись почему падает и исправь если причина очевидна", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "нет не UPO а UPO2", wantSpeech: interaction.SpeechActCorrection, wantCognition: false},
|
||
{message: "нет не UPO а UPO2 и обнови конфиг", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "postgres сейчас работает", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "сейчас домовой не видит ии", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "домовой не видит ии", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "основная база называется UPO", wantSpeech: interaction.SpeechActAssertion, wantCognition: false},
|
||
{message: "привет", wantSpeech: interaction.SpeechActConversational, wantCognition: false},
|
||
{message: "спасибо", wantSpeech: interaction.SpeechActConversational, wantCognition: false},
|
||
{message: "ты кто", wantSpeech: interaction.SpeechActQuestion, wantCognition: true},
|
||
{message: "Ты кто?", wantSpeech: interaction.SpeechActQuestion, wantCognition: true},
|
||
{message: "как тебя зовут", wantSpeech: interaction.SpeechActQuestion, wantCognition: true},
|
||
{message: "кто ты вообще", wantSpeech: interaction.SpeechActQuestion, wantCognition: true},
|
||
{message: "что ты думаешь", wantSpeech: interaction.SpeechActQuestion, wantCognition: true},
|
||
{message: "расскажи подробнее", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "СЕЙЧАС ДОМОВОЙ НЕ ВИДИТ ИИ", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "не понимаю меня не понимает ии", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "домовой не вижит ии", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "домовой не отвечает мне", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "домовой не слушает меня", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "домовой не отвечает на команды", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
{message: "ПОЧЕМУ postgres использует так много памяти", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||
}
|
||
for _, tc := range cases {
|
||
result := interpretForTest(t, tc.message, nil, "")
|
||
if tc.wantSpeech != "" && result.Interpretation.SpeechAct != tc.wantSpeech {
|
||
t.Fatalf("message %q expected speech=%s got %s", tc.message, tc.wantSpeech, result.Interpretation.SpeechAct)
|
||
}
|
||
if tc.wantSpeech == "" && !tc.allowNoMatch && result.Interpretation.SpeechAct == "" {
|
||
t.Fatalf("message %q got empty speech interpretation", tc.message)
|
||
}
|
||
if result.NeedsCognition != tc.wantCognition {
|
||
t.Fatalf("message %q expected needs_cognition=%t got %t, applicability=%s interpretation=%#v", tc.message, tc.wantCognition, result.NeedsCognition, result.Interpretation.Applicability, result.Interpretation)
|
||
}
|
||
if tc.message != "" && result.Interpretation.Clarification == interaction.ClarificationRequired && tc.wantCognition {
|
||
t.Fatalf("message %q unexpectedly required clarification while asking for safe conversational understanding", tc.message)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceDiagnosticTraceLineage(t *testing.T) {
|
||
message := "ты кто"
|
||
conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen}
|
||
utterance, _, _, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: message}, time.Unix(100, 0))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
result, err := InterpretUtterance(utterance, projectRegistryForTest(), nil, time.Unix(100, 0))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
candidateID, err := ids.New()
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
cognitiveCandidate := interaction.SemanticCandidate{
|
||
ID: interaction.SemanticCandidateID(candidateID),
|
||
UtteranceID: utterance.ID,
|
||
Source: interaction.CandidateSourceCognitive,
|
||
SelectionState: interaction.CandidateObserved,
|
||
PrimarySpeechAct: interaction.SpeechActQuestion,
|
||
SecondarySpeechAct: interaction.SpeechActUnknown,
|
||
ConfidenceMillis: 780,
|
||
Applicability: interaction.SemanticNeedsBroaderInterpret,
|
||
RequestedOutcome: "",
|
||
Clarification: interaction.ClarificationNotRequired,
|
||
NeedsCognition: true,
|
||
SelectionReason: "trace-oriented cognitive clarification of identity-style intent",
|
||
CapabilityHints: []string{"COGNITIVE_REASONING"},
|
||
RecordedAtUnixMillis: time.Unix(100, 0).UnixMilli(),
|
||
}
|
||
selected := ApplyCognitiveCandidate(result, message, cognitiveCandidate, nil)
|
||
var deterministic, selectedCandidate *interaction.SemanticCandidate
|
||
for index := range selected.Candidates {
|
||
c := &selected.Candidates[index]
|
||
if c.Source == interaction.CandidateSourceDeterministic {
|
||
deterministic = c
|
||
}
|
||
if c.SelectionState == interaction.CandidateSelected {
|
||
selectedCandidate = c
|
||
}
|
||
}
|
||
if deterministic == nil {
|
||
t.Fatal("expected deterministic candidate in trace")
|
||
}
|
||
if selectedCandidate == nil {
|
||
t.Fatal("expected selected candidate in trace")
|
||
}
|
||
if selected.Interpretation.Source != string(interaction.CandidateSourceCognitive) {
|
||
t.Fatalf("selection source should switch to cognitive, got %s", selected.Interpretation.Source)
|
||
}
|
||
if selected.Interpretation.NeedsCognition != selectedCandidate.NeedsCognition {
|
||
t.Fatalf("expected selected interpretation cognition=%t got %t", selectedCandidate.NeedsCognition, selected.Interpretation.NeedsCognition)
|
||
}
|
||
if selected.Interpretation.Clarification != selectedCandidate.Clarification {
|
||
t.Fatalf("expected selected candidate clarification to match interpretation clarification")
|
||
}
|
||
if selectedCandidate.SelectionReason == "" {
|
||
t.Fatal("expected clarification/selection reason in selected trace")
|
||
}
|
||
t.Logf("TRACE deterministic_source=%s cognitive_source=%s selected_source=%s needs_cognition=%t clarification_reason=%s",
|
||
deterministic.Source, selectedCandidate.Source, selected.Interpretation.Source, selected.Interpretation.NeedsCognition, selected.Interpretation.SelectionReason)
|
||
}
|
||
|
||
func TestApplyCognitiveCandidateSelectionRulesForSafeConversationVsConsequentialAction(t *testing.T) {
|
||
conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen}
|
||
safeUtterance, _, _, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: "ты кто"}, time.Unix(100, 0))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
safeSemantic, err := InterpretUtterance(safeUtterance, projectRegistryForTest(), nil, time.Unix(100, 0))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
safeCandidateID, err := ids.New()
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
safeCognitive := interaction.SemanticCandidate{
|
||
ID: interaction.SemanticCandidateID(safeCandidateID),
|
||
UtteranceID: safeUtterance.ID,
|
||
Source: interaction.CandidateSourceCognitive,
|
||
SelectionState: interaction.CandidateObserved,
|
||
PrimarySpeechAct: interaction.SpeechActAssertion,
|
||
SecondarySpeechAct: interaction.SpeechActUnknown,
|
||
ConfidenceMillis: 770,
|
||
Applicability: interaction.SemanticNeedsBroaderInterpret,
|
||
RequestedOutcome: "",
|
||
Clarification: interaction.ClarificationNotRequired,
|
||
NeedsCognition: true,
|
||
SelectionReason: "identity-style trace test",
|
||
CapabilityHints: []string{"COGNITIVE_REASONING"},
|
||
RecordedAtUnixMillis: time.Unix(100, 0).UnixMilli(),
|
||
}
|
||
safeSelected := ApplyCognitiveCandidate(safeSemantic, safeUtterance.Content, safeCognitive, nil)
|
||
if safeSelected.Interpretation.Source != string(interaction.CandidateSourceCognitive) {
|
||
t.Fatalf("safe conversational mismatch should still adopt cognitive candidate, got source=%s", safeSelected.Interpretation.Source)
|
||
}
|
||
if safeSelected.Interpretation.SpeechAct != interaction.SpeechActAssertion {
|
||
t.Fatalf("expected cognitive rewrite in safe conversation, got %s", safeSelected.Interpretation.SpeechAct)
|
||
}
|
||
|
||
riskyUtterance, _, _, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: "удали УПО"}, time.Unix(100, 0))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
riskySemantic, err := InterpretUtterance(riskyUtterance, projectRegistryForTest(), nil, time.Unix(100, 0))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if riskySemantic.Interpretation.SpeechAct != interaction.SpeechActDirective {
|
||
t.Fatalf("expected directive baseline, got %s", riskySemantic.Interpretation.SpeechAct)
|
||
}
|
||
riskyCandidateID, err := ids.New()
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
riskyCognitive := interaction.SemanticCandidate{
|
||
ID: interaction.SemanticCandidateID(riskyCandidateID),
|
||
UtteranceID: riskyUtterance.ID,
|
||
Source: interaction.CandidateSourceCognitive,
|
||
SelectionState: interaction.CandidateObserved,
|
||
PrimarySpeechAct: interaction.SpeechActQuestion,
|
||
SecondarySpeechAct: interaction.SpeechActUnknown,
|
||
ConfidenceMillis: 780,
|
||
Applicability: interaction.SemanticNeedsBroaderInterpret,
|
||
RequestedOutcome: "",
|
||
Clarification: interaction.ClarificationNotRequired,
|
||
NeedsCognition: true,
|
||
SelectionReason: "destructive path mismatch guard",
|
||
CapabilityHints: []string{"COGNITIVE_REASONING"},
|
||
RecordedAtUnixMillis: time.Unix(100, 0).UnixMilli(),
|
||
}
|
||
riskySelected := ApplyCognitiveCandidate(riskySemantic, riskyUtterance.Content, riskyCognitive, nil)
|
||
if riskySelected.Interpretation.Source != string(interaction.CandidateSourceDeterministic) {
|
||
t.Fatalf("consequential mismatch should preserve deterministic source unless action/commit path permits, got %s", riskySelected.Interpretation.Source)
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceModalExplanationRequestStillRequestsCognition(t *testing.T) {
|
||
for _, message := range []string{"можешь расказать об утре", "расскажи об утре", "про утро расскажешь"} {
|
||
result := interpretForTest(t, message, nil, "")
|
||
if result.Interpretation.SpeechAct == interaction.SpeechActAssertion && result.Interpretation.Applicability == interaction.SemanticApplicable {
|
||
t.Fatalf("expected cognition-worthy interpretation for %q, got %#v", message, result.Interpretation)
|
||
}
|
||
if !result.NeedsCognition {
|
||
t.Fatalf("expected cognition route for %q, got %#v", message, result)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestInterpretUtteranceSelectsDeterministicCandidateByDefault(t *testing.T) {
|
||
result := interpretForTest(t, "основная база называется UPO", nil, "")
|
||
if len(result.Candidates) != 1 {
|
||
t.Fatalf("expected one deterministic candidate, got %d", len(result.Candidates))
|
||
}
|
||
if result.Candidates[0].SelectionState != interaction.CandidateSelected {
|
||
t.Fatalf("deterministic candidate must be selected by default, got %s", result.Candidates[0].SelectionState)
|
||
}
|
||
}
|
||
|
||
func TestRejectedCognitiveCandidatePreservesSelectedDeterministicCandidate(t *testing.T) {
|
||
result := interpretForTest(t, "основная база называется UPO", nil, "")
|
||
candidateID, err := ids.New()
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
rejected := interaction.SemanticCandidate{
|
||
ID: interaction.SemanticCandidateID(candidateID),
|
||
UtteranceID: result.Interpretation.UtteranceID,
|
||
Source: interaction.CandidateSourceCognitive,
|
||
PrimarySpeechAct: interaction.SpeechActQuestion,
|
||
SecondarySpeechAct: interaction.SpeechActUnknown,
|
||
ConfidenceMillis: 700,
|
||
Applicability: interaction.SemanticApplicable,
|
||
Clarification: interaction.ClarificationNotRequired,
|
||
NeedsCognition: false,
|
||
SelectionReason: "must not replace a safe assertion with an unrelated candidate",
|
||
}
|
||
result = ApplyCognitiveCandidate(result, "основная база называется UPO", rejected, nil)
|
||
selected := 0
|
||
for _, candidate := range result.Candidates {
|
||
if candidate.SelectionState == interaction.CandidateSelected {
|
||
selected++
|
||
if candidate.Source != interaction.CandidateSourceDeterministic {
|
||
t.Fatalf("rejected cognitive candidate replaced deterministic selection: %#v", candidate)
|
||
}
|
||
}
|
||
}
|
||
if selected != 1 {
|
||
t.Fatalf("expected exactly one selected candidate after rejection, got %d", selected)
|
||
}
|
||
}
|
||
|
||
func interpretForTest(t *testing.T, message string, previous *situation.ActiveProjectFocus, corrects interaction.HumanUtteranceID) SemanticResult {
|
||
t.Helper()
|
||
conversation := interaction.Conversation{ID: "conversation-1", ActorID: "actor-1", PrincipalID: "principal-1", Channel: interaction.ChannelWebChat, State: interaction.ConversationOpen}
|
||
utterance, _, _, err := NewHumanUtterance(conversation, interaction.ChatMessageRequest{Message: message, CorrectsUtteranceID: corrects}, time.Unix(100, 0))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
result, err := InterpretUtterance(utterance, projectRegistryForTest(), previous, time.Unix(100, 0))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
return result
|
||
}
|
||
|
||
func projectRegistryForTest() []situation.ProjectDescriptor {
|
||
return []situation.ProjectDescriptor{
|
||
{ID: "assistant", DisplayName: "Assistant", Aliases: []situation.ProjectAlias{"assistant", "ассистент", "домовой"}},
|
||
{ID: "upo", DisplayName: "УПО", Aliases: []situation.ProjectAlias{"упо", "upo"}},
|
||
}
|
||
}
|
||
|
||
func activeFocus(projectID situation.ProjectID, displayName string) *situation.ActiveProjectFocus {
|
||
return &situation.ActiveProjectFocus{
|
||
ActorID: identity.ActorID("actor-1"),
|
||
PrincipalID: identity.PrincipalID("principal-1"),
|
||
ProjectID: projectID,
|
||
ProjectDisplayName: displayName,
|
||
Provenance: situation.ProjectResolutionExplicit,
|
||
ConfidenceMillis: 980,
|
||
SourceUtteranceID: "utterance-focus",
|
||
UpdatedAtUnixMillis: time.Unix(90, 0).UnixMilli(),
|
||
}
|
||
}
|