diff --git a/cmd/core-runtime/main.go b/cmd/core-runtime/main.go index 63bcd80..3419160 100644 --- a/cmd/core-runtime/main.go +++ b/cmd/core-runtime/main.go @@ -571,7 +571,8 @@ func main() { semantic.NeedsCognition = false semantic.Interpretation.NeedsCognition = false } - if locationReply := interactiondomain.LocationContextReply(utterance.Content); locationReply != "" { + locationReply := interactiondomain.LocationContextReply(utterance.Content) + if locationReply != "" { semantic.Reply = locationReply semantic.NeedsCognition = false semantic.Interpretation.NeedsCognition = false @@ -584,12 +585,7 @@ func main() { } assistantName := interactiondomain.PreferredAssistantName(utterance.Content) if assistantName == "" { - for index := len(conversationHistory) - 1; index >= 0; index-- { - if name := interactiondomain.PreferredAssistantName(conversationHistory[index].Content); name != "" { - assistantName = name - break - } - } + assistantName = latestAssistantName(conversationHistory) } identityReply := "" log.Printf("interaction identity context: name=%q history=%d", assistantName, len(conversationHistory)) @@ -871,7 +867,7 @@ func main() { ConfidenceMillis: semantic.Interpretation.ConfidenceMillis, }) cancelReview() - stableSemanticReply := identityReply != "" || deterministicSpeechAct == interaction.SpeechActConversational || deterministicSpeechAct == interaction.SpeechActConfirmation || deterministicSpeechAct == interaction.SpeechActRejection + stableSemanticReply := identityReply != "" || semantic.InformationRequestKind != "" || locationReply != "" || deterministicSpeechAct == interaction.SpeechActConversational || deterministicSpeechAct == interaction.SpeechActConfirmation || deterministicSpeechAct == interaction.SpeechActRejection if reviewErr != nil { log.Printf("cognitive response review unavailable: %v", reviewErr) } else if !stableSemanticReply && (!researchBacked || reviewPreservesResearchEvidence(reviewedReply, planningReply)) { @@ -1672,20 +1668,37 @@ func humanMemoryInventory(history []interaction.HumanUtterance, assistantName st if assistantName != "" { rows = append(rows, "| Имя помощника | "+assistantName+" |") } - for index := len(history) - 1; index >= 0; index-- { - message := strings.TrimSpace(history[index].Content) + latestPreferenceAt := int64(-1) + latestPreference := "" + for _, utterance := range history { + message := strings.TrimSpace(utterance.Content) lower := strings.ToLower(message) - if strings.Contains(lower, "нравится") || strings.Contains(lower, "люблю") { - rows = append(rows, "| Предпочтение пользователя | "+message+" |") - break + if (strings.Contains(lower, "нравится") || strings.Contains(lower, "люблю")) && utterance.ReceivedAtUnixMillis >= latestPreferenceAt { + latestPreferenceAt = utterance.ReceivedAtUnixMillis + latestPreference = message } } + if latestPreference != "" { + rows = append(rows, "| Предпочтение пользователя | "+latestPreference+" |") + } if len(rows) == 0 { return "| Ячейка памяти | Значение |\n|---|---|\n| — | Сохранённых пользовательских предпочтений пока не найдено. |" } return "| Ячейка памяти | Значение |\n|---|---|\n" + strings.Join(rows, "\n") } +func latestAssistantName(history []interaction.HumanUtterance) string { + latestAt := int64(-1) + latestName := "" + for _, utterance := range history { + if name := interactiondomain.PreferredAssistantName(utterance.Content); name != "" && utterance.ReceivedAtUnixMillis >= latestAt { + latestAt = utterance.ReceivedAtUnixMillis + latestName = name + } + } + return latestName +} + func humanTimeDifferenceReply(message string) string { lower := strings.ToLower(message) if strings.Contains(lower, "моск") && strings.Contains(lower, "шанх") { diff --git a/cmd/core-runtime/main_test.go b/cmd/core-runtime/main_test.go index 183a308..9f37a35 100644 --- a/cmd/core-runtime/main_test.go +++ b/cmd/core-runtime/main_test.go @@ -27,6 +27,27 @@ func TestBearerTokenRequiresBearerScheme(t *testing.T) { } } +func TestLatestAssistantNameUsesNewestDurablePreference(t *testing.T) { + history := []interaction.HumanUtterance{ + {Content: "я назову тебя Дед", ReceivedAtUnixMillis: 20}, + {Content: "теперь тебя зовут Фея", ReceivedAtUnixMillis: 30}, + } + if got := latestAssistantName(history); got != "Фея" { + t.Fatalf("latest assistant name = %q", got) + } +} + +func TestHumanMemoryInventoryUsesNewestPreference(t *testing.T) { + history := []interaction.HumanUtterance{ + {Content: "мне нравится синий цвет", ReceivedAtUnixMillis: 20}, + {Content: "мне нравится красный цвет", ReceivedAtUnixMillis: 30}, + } + got := humanMemoryInventory(history, "Фея") + if !strings.Contains(got, "красный") || strings.Contains(got, "синий") { + t.Fatalf("memory inventory did not select newest preference: %q", got) + } +} + func TestCognitiveChannelQuestionUsesHumanSafeHealthReply(t *testing.T) { reply := humanCognitiveChannelReply(interaction.InformationRequestCognitiveChannelStatus, interaction.SpeechActQuestion, capability.Health{State: capability.HealthAvailable, CapabilityID: "provider-secret-model"}, nil) if !strings.Contains(reply, "доступен") || strings.Contains(reply, "provider-secret-model") {