Compare commits

...
Author SHA1 Message Date
m 1d73162df1 Merge remote-tracking branch 'origin/main' into codex/household-travel-context-fix
CI / test (push) Successful in 1m14s
CI / test (pull_request) Successful in 1m24s
2026-08-25 19:17:59 +03:00
m 7788cf4b44 Preserve location context for travel follow-ups
CI / test (push) Failing after 58s
CI / test (pull_request) Failing after 50s
2026-08-25 19:17:34 +03:00
m d9dcddbdbb Merge pull request 'Preserve direct time difference replies' (#367) from codex/household-time-reply-priority into main
CI / test (push) Successful in 2m3s
Reviewed-on: #367
2026-08-25 16:11:36 +00:00
m 5fb616310d Merge remote-tracking branch 'origin/main' into codex/household-time-reply-priority
CI / test (push) Successful in 1m18s
CI / test (pull_request) Successful in 1m24s
2026-08-25 19:08:15 +03:00
m 5a9cf7a22d Preserve direct time difference replies
CI / test (push) Failing after 51s
CI / test (pull_request) Failing after 48s
2026-08-25 19:06:51 +03:00
m d649f79ca4 Merge pull request 'Preserve remembered preference identity' (#366) from codex/household-checklist-live-preference-fix into main
CI / test (push) Successful in 1m58s
Reviewed-on: #366
2026-08-25 16:00:08 +00:00
4 changed files with 60 additions and 4 deletions
@@ -0,0 +1,25 @@
package main
import (
"testing"
"git.cin.su/m/Assistant/contracts/interaction"
)
func TestSpecializedInformationReplyIsNotOverwrittenByResearch(t *testing.T) {
for _, kind := range []interaction.InformationRequestKind{
interaction.InformationRequestMemoryInventory,
interaction.InformationRequestTimeDifference,
interaction.InformationRequestCapabilityInventory,
interaction.InformationRequestCognitiveModelInventory,
interaction.InformationRequestCognitiveChannelStatus,
interaction.InformationRequestCognitiveRecovery,
} {
if shouldRunGeneralResearch(kind) {
t.Fatalf("specialized information request %q must bypass general research", kind)
}
}
if !shouldRunGeneralResearch("") {
t.Fatal("ordinary research requests must still use general research")
}
}
+12 -4
View File
@@ -511,6 +511,7 @@ func main() {
}
var effectiveTaskMessage = interactiondomain.StripAssistantVocative(utterance.Content)
creativeUserRequest := interactiondomain.IsCreativeRequest(utterance.Content)
locationReply := interactiondomain.LocationContextReply(utterance.Content)
continuationMessage := ""
travelContinuation := false
var previousTask *interaction.HumanUtterance
@@ -532,7 +533,7 @@ func main() {
log.Printf("interaction principal history loaded: %d utterances", len(recentUserHistory))
}
conversationHistory = append(conversationHistory, recentUserHistory...)
if !creativeUserRequest {
if !creativeUserRequest && locationReply == "" {
if continuation := interactiondomain.ContinueLocalSearch(effectiveTaskMessage, conversationHistory); continuation != "" {
continuationMessage = continuation
effectiveTaskMessage = continuation
@@ -599,8 +600,11 @@ func main() {
}
}
}
locationReply := interactiondomain.LocationContextReply(utterance.Content)
if locationReply != "" {
continuationMessage = ""
travelContinuation = false
effectiveTaskMessage = interactiondomain.StripAssistantVocative(utterance.Content)
previousTask = nil
semantic.Reply = locationReply
semantic.NeedsCognition = false
semantic.Interpretation.NeedsCognition = false
@@ -769,7 +773,7 @@ func main() {
needsCognition = false
}
researchBacked := false
if hasChatRoute(routePlan, chatRouteResearch) && semantic.Interpretation.Clarification != interaction.ClarificationRequired {
if hasChatRoute(routePlan, chatRouteResearch) && semantic.Interpretation.Clarification != interaction.ClarificationRequired && shouldRunGeneralResearch(semantic.InformationRequestKind) {
if planID, idErr := ids.New(); idErr == nil {
parts := interactiondomain.IndependentRequestParts(effectiveTaskMessage)
replies := make([]string, 0, len(parts))
@@ -778,7 +782,7 @@ func main() {
replies = append(replies, "По запросу «"+part+"»: "+clarification)
continue
}
searchQuery := interactiondomain.TravelSearchQuery(part, now)
searchQuery := interactiondomain.TravelResearchQuery(part, conversationHistory, now)
researchCtx, cancelResearch := context.WithTimeout(r.Context(), 8*time.Second)
result, researchErr := researchClient.SearchWeb(researchCtx, epistemics.WebSearchRequest{
Plan: epistemics.ResearchPlan{ID: epistemics.ResearchPlanID(fmt.Sprintf("plan:%s:%d", planID, index)), Question: searchQuery, SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"},
@@ -1589,6 +1593,10 @@ func main() {
}
}
func shouldRunGeneralResearch(kind interaction.InformationRequestKind) bool {
return kind == ""
}
func creatorSessionHandler(bootstrapToken, creatorUsername, creatorPassword string, sessionKey []byte, now func() time.Time) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
authenticated := identitydomain.BootstrapMatches(bootstrapToken, bearerToken(r))
@@ -159,6 +159,14 @@ func TestTravelSearchQueryCarriesNearestFlightDate(t *testing.T) {
}
}
func TestTravelResearchQueryUsesKnownOriginForDistance(t *testing.T) {
history := []interaction.HumanUtterance{{Content: "я сейчас в Москве"}}
got := TravelResearchQuery("сколько километров до Сочи?", history, time.Time{})
if !strings.Contains(got, "точка отправления: Москве") || !strings.Contains(got, "расстояние по прямой и по дороге") {
t.Fatalf("distance query lost route context: %q", got)
}
}
func TestInterpretUtteranceDirectiveRecordsOutcomeWithoutExecution(t *testing.T) {
result := interpretForTest(t, "Поменяй базу на УПО1.", activeFocus("upo", "УПО"), "")
if result.Interpretation.SpeechAct != interaction.SpeechActDirective {
+15
View File
@@ -21,6 +21,21 @@ func TravelSearchQuery(message string, now time.Time) string {
return cleaned + " сегодня " + now.Format("02.01.2006")
}
// TravelResearchQuery keeps route and distance questions tied to a known
// origin before they reach a generic web search. The history is semantic
// context, not a list of example-specific answers.
func TravelResearchQuery(message string, history []interaction.HumanUtterance, now time.Time) string {
query := TravelSearchQuery(message, now)
tokens := semanticTokens(StripAssistantVocative(message))
if containsStemInTokens(tokens, "расстоян", "километр", "добрат", "маршрут") {
if origin := travelContextOrigin(history); origin != "" && !containsStemInTokens(tokens, "из", "откуда", "от") {
query += " точка отправления: " + origin
}
query += " расстояние по прямой и по дороге"
}
return query
}
// StripAssistantVocative removes a leading assistant name used as an address.
// The original utterance remains the historical source; this value is only for
// interpretation, cognitive prompts and external queries.