Compare commits

..
Author SHA1 Message Date
m a925d787dc GEN2-027: prepare controlled production canary
CI / test (push) Failing after 26s
2026-08-29 12:01:37 +03:00
m 43556e1603 GEN2-026: route household context through scoped Gen2 projection
CI / test (push) Failing after 28s
2026-08-29 12:00:04 +03:00
4 changed files with 30 additions and 2 deletions
+1 -1
View File
@@ -533,7 +533,7 @@ func main() {
log.Printf("interaction principal history loaded: %d utterances", len(recentUserHistory))
}
conversationHistory = append(conversationHistory, recentUserHistory...)
gen2Context := interactiondomain.BuildGen2Context(conversationHistory)
gen2Context := interactiondomain.BuildScopedGen2Context(conversationHistory, string(session.PrincipalID))
if !creativeUserRequest && locationReply == "" {
if continuation := interactiondomain.ContinueLocalSearch(effectiveTaskMessage, conversationHistory); continuation != "" {
continuationMessage = continuation
+1 -1
View File
@@ -1 +1 @@
{"task_file":"dev/tasks/GEN2-025-multimodal-and-ambient-ingress-readiness-without-implementing-the-whole-.json","task_id":"GEN2-025","task_revision":1}
{"task_file":"dev/tasks/GEN2-027-production-canary-and-controlled-gen2-traffic-transition.json","task_id":"GEN2-027","task_revision":1}
@@ -11,6 +11,23 @@ type ContextProjection struct {
// BuildGen2Context is a read-side projection over canonical utterance history.
// PersonalMemory remains a compatibility fallback and is never written here.
func BuildGen2Context(history []interaction.HumanUtterance) ContextProjection {
return BuildScopedGen2Context(history, "")
}
// BuildScopedGen2Context is the Gen2 retrieval boundary for household
// context. The caller supplies already-authorized history; this defensive
// filter prevents accidental cross-principal projection when a mixed source
// is passed during degraded operation.
func BuildScopedGen2Context(history []interaction.HumanUtterance, principalID string) ContextProjection {
if principalID != "" {
scoped := make([]interaction.HumanUtterance, 0, len(history))
for _, item := range history {
if item.PrincipalID == "" || string(item.PrincipalID) == principalID {
scoped = append(scoped, item)
}
}
history = scoped
}
text := CompilePersonalMemory(history).ContextText()
if text != "" {
return ContextProjection{Text: text, Source: "retrieval:interaction-utterances"}
@@ -3,6 +3,7 @@ package domain_test
import (
"git.cin.su/m/Assistant/contracts/interaction"
"git.cin.su/m/Assistant/modules/interaction/domain"
"strings"
"testing"
)
@@ -16,3 +17,13 @@ func TestGen2ContextUsesRetrievalProjectionAndKeepsV1Fallback(t *testing.T) {
t.Fatalf("fallback missing: %#v", empty)
}
}
func TestGen2ContextDoesNotProjectAnotherPrincipal(t *testing.T) {
got := domain.BuildScopedGen2Context([]interaction.HumanUtterance{
{ID: "mine", PrincipalID: "principal-1", Content: "Я сейчас в Москве", ReceivedAtUnixMillis: 2},
{ID: "other", PrincipalID: "principal-2", Content: "Я сейчас в Казани", ReceivedAtUnixMillis: 3},
}, "principal-1")
if got.Text == "" || strings.Contains(got.Text, "Казани") || !strings.Contains(got.Text, "Москве") {
t.Fatalf("scoped context=%#v", got)
}
}