dialogue: keep the remembered topic on the latest turn
rememberTurn runs after followUpMerge, which has already inherited a Text slot from the previous same-intent turn, so the fill-if-empty rule pinned the first topic of a run of query turns and never released it. "во сколько у меня встреча", then "какие у меня планы", then "а завтра?" continued the meeting — two turns stale. Overwrite for system and query, where Text is a topic and not a payload. A continuation is the exception and keeps what it inherited: its own utterance is the ellipsis, and the topic it carries is the real one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TrVSBKe3RFDF4fGYKWYQnX
This commit is contained in:
+10
-1
@@ -353,8 +353,17 @@ func (h *reactiveHandler) rememberTurn(prev *dialogue.Session, dec router.Decisi
|
||||
// which knows the day but not what was asked ABOUT, so keep the raw
|
||||
// utterance where continuation.go can find it. Only these two intents:
|
||||
// everywhere else Text is a payload and must stay what the router put in.
|
||||
//
|
||||
// Overwritten, not filled: rememberTurn runs AFTER followUpMerge, which
|
||||
// has already inherited a Text from the previous same-intent turn, so a
|
||||
// fill-if-empty rule keeps the OLD topic for ever. Seen on the deployed
|
||||
// daemon 01-08-2026 — "во сколько у меня встреча" then "какие у меня
|
||||
// планы" then "а завтра?" continued the meeting, two turns stale.
|
||||
//
|
||||
// A continuation is the exception and keeps what it inherited: its
|
||||
// utterance is the ellipsis, and the topic it carries is the real one.
|
||||
slots := toDialogueSlots(dec.Slots)
|
||||
if slots.Text == "" && (dec.Intent == router.IntentSystem || dec.Intent == router.IntentQuery) {
|
||||
if !dec.Continued && (dec.Intent == router.IntentSystem || dec.Intent == router.IntentQuery) {
|
||||
slots.Text = dec.Utterance
|
||||
}
|
||||
h.dialogueSessions.Put(voiceDialogueID, &dialogue.Session{
|
||||
|
||||
@@ -138,3 +138,32 @@ func TestReplySystemIgnoresAnInheritedTopic(t *testing.T) {
|
||||
t.Fatalf("replySystem refused a real continuation")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRememberTurnRefreshesTheTopic — rememberTurn runs after followUpMerge,
|
||||
// which has already inherited a Text from the previous same-intent turn. A
|
||||
// fill-if-empty rule therefore pins the FIRST topic of a run of query turns
|
||||
// and never lets go, so a later "а завтра?" continues a question two turns
|
||||
// old. Seen on the deployed daemon, 01-08-2026.
|
||||
func TestRememberTurnRefreshesTheTopic(t *testing.T) {
|
||||
h := &reactiveHandler{
|
||||
now: func() time.Time { return contNow },
|
||||
dialogueSessions: dialogue.NewSessionStore(2 * time.Minute),
|
||||
}
|
||||
h.rememberTurn(nil, router.Decision{
|
||||
Intent: router.IntentQuery, Utterance: "во сколько у меня встреча",
|
||||
}, contNow)
|
||||
// The second turn arrives with the first turn's Text already merged in.
|
||||
prev := h.dialogueSessions.Get(voiceDialogueID, contNow)
|
||||
h.rememberTurn(prev, router.Decision{
|
||||
Intent: router.IntentQuery,
|
||||
Utterance: "какие у меня планы",
|
||||
Slots: router.Slots{Text: "во сколько у меня встреча"},
|
||||
}, contNow)
|
||||
got := h.dialogueSessions.Get(voiceDialogueID, contNow)
|
||||
if got == nil {
|
||||
t.Fatal("no session")
|
||||
}
|
||||
if got.Slots.Text != "какие у меня планы" {
|
||||
t.Fatalf("topic = %q, want the latest turn's", got.Slots.Text)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user