Let her ask three times, and let a restated answer win
MaxAttempts was 1, justified as "not a nag". Wrong reading: "not a nag" is about interrupting unprompted, and a clarifying question is part of a conversation he started. Now three, configurable via voice.clarify_max_attempts (default 3). Three, because after that the likely problem is she misheard the whole request, not one slot. Answer used to keep the parked value, so "в три" then "нет, в пять" threw the five away. Now a value the answer carries wins for the slot she asked about. Only for the clarify answer — a correction in a fresh turn is followUpMerge. The eight-field chained assertion in the Answer test is one DeepEqual now, so a new field in Slots is covered without touching the test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package dialogue
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -89,12 +90,13 @@ func TestAnswerFillsOnlyMissingSlots(t *testing.T) {
|
||||
want: Slots{Text: "напомни позвонить", Time: answerTime, HasTime: true},
|
||||
},
|
||||
{
|
||||
name: "does not overwrite a filled time",
|
||||
// He restated it: «нет, в пять». The new value wins.
|
||||
name: "a restated time overwrites the parked one",
|
||||
parked: Slots{Time: other, HasTime: true},
|
||||
missing: []Slot{SlotTime},
|
||||
text: "в три",
|
||||
text: "нет, в три",
|
||||
answer: Slots{Time: answerTime, HasTime: true},
|
||||
want: Slots{Time: other, HasTime: true},
|
||||
want: Slots{Time: answerTime, HasTime: true},
|
||||
},
|
||||
{
|
||||
name: "ignores slots that were not missing",
|
||||
@@ -121,12 +123,12 @@ func TestAnswerFillsOnlyMissingSlots(t *testing.T) {
|
||||
want: Slots{Fn: "restart", Args: []string{"nginx"}, HasFn: true},
|
||||
},
|
||||
{
|
||||
name: "keeps existing args when fn was already known",
|
||||
name: "a restated fn replaces the fn and its args",
|
||||
parked: Slots{Fn: "restart", Args: []string{"nginx"}, HasFn: true},
|
||||
missing: []Slot{SlotFn},
|
||||
text: "останови postgres",
|
||||
answer: Slots{Fn: "stop", Args: []string{"postgres"}, HasFn: true},
|
||||
want: Slots{Fn: "restart", Args: []string{"nginx"}, HasFn: true},
|
||||
want: Slots{Fn: "stop", Args: []string{"postgres"}, HasFn: true},
|
||||
},
|
||||
{
|
||||
name: "raw answer becomes the text when nothing was parsed",
|
||||
@@ -157,36 +159,34 @@ func TestAnswerFillsOnlyMissingSlots(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
q := &PendingQuestion{Slots: tc.parked, Missing: tc.missing, Asked: base}
|
||||
got := q.Answer(tc.text, tc.answer)
|
||||
if got.Time != tc.want.Time || got.HasTime != tc.want.HasTime ||
|
||||
got.Key != tc.want.Key || got.HasKey != tc.want.HasKey ||
|
||||
got.Value != tc.want.Value || got.Text != tc.want.Text ||
|
||||
got.Fn != tc.want.Fn || got.HasFn != tc.want.HasFn {
|
||||
// Whole-struct compare: a new field in Slots is covered for free.
|
||||
if got := q.Answer(tc.text, tc.answer); !reflect.DeepEqual(got, tc.want) {
|
||||
t.Fatalf("Answer = %+v, want %+v", got, tc.want)
|
||||
}
|
||||
if len(got.Args) != len(tc.want.Args) {
|
||||
t.Fatalf("Args = %v, want %v", got.Args, tc.want.Args)
|
||||
}
|
||||
for i := range got.Args {
|
||||
if got.Args[i] != tc.want.Args[i] {
|
||||
t.Fatalf("Args = %v, want %v", got.Args, tc.want.Args)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanAskCapsAtOneQuestion(t *testing.T) {
|
||||
if MaxAttempts != 1 {
|
||||
t.Fatalf("MaxAttempts = %d, want 1 (Maven asks once, she is not a nag)", MaxAttempts)
|
||||
func TestCanAskAllowsThreeQuestionsByDefault(t *testing.T) {
|
||||
if DefaultMaxAttempts != 3 {
|
||||
t.Fatalf("DefaultMaxAttempts = %d, want 3", DefaultMaxAttempts)
|
||||
}
|
||||
q := &PendingQuestion{Asked: base}
|
||||
if !q.CanAsk() {
|
||||
t.Fatal("a fresh question should be askable")
|
||||
q := &PendingQuestion{Asked: base} // MaxAttempts unset ⇒ the default
|
||||
for i := 0; i < 3; i++ {
|
||||
if !q.CanAsk() {
|
||||
t.Fatalf("question %d should be allowed", i+1)
|
||||
}
|
||||
q.Attempts++
|
||||
}
|
||||
q.Attempts = MaxAttempts
|
||||
if q.CanAsk() {
|
||||
t.Fatal("the question should not be asked twice")
|
||||
t.Fatal("a fourth question must not be allowed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanAskHonoursConfiguredMax(t *testing.T) {
|
||||
q := &PendingQuestion{Asked: base, MaxAttempts: 1, Attempts: 1}
|
||||
if q.CanAsk() {
|
||||
t.Fatal("MaxAttempts 1 means one question only")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user