77206f298e
The capture half landed with the grammar in 87d1761. This is the exposure
the task asked to check for: IsTaskListQuery is a deterministic lookup that
only runs once the turn is already a query, so a phrasing the model calls
system never reaches it. The eval fixture was also missing both grammars,
which is only worth having while it is the daemon's grammar set.
146 lines
6.4 KiB
Go
146 lines
6.4 KiB
Go
package router
|
|
|
|
import "testing"
|
|
|
|
func TestParseTaskCapture(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
text string
|
|
weight int
|
|
ok bool
|
|
}{
|
|
{"добавь в задачи купить молоко", "купить молоко", 0, true},
|
|
{"Добавь в список дел: позвонить в банк", "позвонить в банк", 0, true},
|
|
{"запиши задачу починить кран.", "починить кран", 0, true},
|
|
{"новая задача — оплатить интернет", "оплатить интернет", 0, true},
|
|
{"add a task buy milk", "buy milk", 0, true},
|
|
// Urgency he stated out loud, leading or trailing, stripped from the text.
|
|
{"добавь в задачи срочно оплатить интернет", "оплатить интернет", 3, true},
|
|
{"добавь в задачи оплатить интернет срочно", "оплатить интернет", 3, true},
|
|
{"новая задача важно позвонить маме", "позвонить маме", 2, true},
|
|
// The stem inside the task text is part of the task, not a marker.
|
|
{"добавь в задачи позвонить в срочную помощь", "позвонить в срочную помощь", 0, true},
|
|
// Whisper punctuates dictated Russian. The marker used to be missed as
|
|
// soon as anything sat next to it, and then it stayed in the task text
|
|
// and in the dedupe key — the exact task he was trying to flag.
|
|
{"добавь в задачи оплатить интернет, срочно", "оплатить интернет", 3, true},
|
|
{"добавь в задачи очень срочно оплатить интернет", "оплатить интернет", 3, true},
|
|
{"добавь в задачи оплатить интернет — важно", "оплатить интернет", 2, true},
|
|
// A dictated question mark is not part of the task.
|
|
{"добавь в задачи позвонить в банк?", "позвонить в банк", 0, true},
|
|
// The phrasings he uses that the prefix list did not have.
|
|
{"поставь задачу вынести мусор", "вынести мусор", 0, true},
|
|
{"добавь в тудушки купить лампочки", "купить лампочки", 0, true},
|
|
// A marker with nothing after it files nothing.
|
|
{"добавь в задачи", "", 0, false},
|
|
{"новая задача", "", 0, false},
|
|
{"добавь в задачи срочно", "", 0, false},
|
|
// Not a capture: he is talking, not filing.
|
|
{"надо бы поспать", "", 0, false},
|
|
{"я не добавил молоко в список", "", 0, false},
|
|
{"какие у меня задачи?", "", 0, false},
|
|
{"", "", 0, false},
|
|
}
|
|
for _, c := range cases {
|
|
got, ok := ParseTaskCapture(c.in)
|
|
if ok != c.ok || got.Text != c.text || got.Weight != c.weight {
|
|
t.Errorf("ParseTaskCapture(%q) = (%+v, %v), want (%q, w=%d, %v)", c.in, got, ok, c.text, c.weight, c.ok)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsTaskListQuery(t *testing.T) {
|
|
yes := []string{
|
|
"какие у меня задачи?",
|
|
"что мне нужно сделать?",
|
|
"покажи список дел",
|
|
"сколько у меня задач?",
|
|
"задачи",
|
|
"мои задачи",
|
|
"what should I do",
|
|
"что мне делать?",
|
|
}
|
|
for _, s := range yes {
|
|
if !IsTaskListQuery(s) {
|
|
t.Errorf("IsTaskListQuery(%q) = false, want true", s)
|
|
}
|
|
}
|
|
no := []string{
|
|
"как дела?",
|
|
// No task noun and no pronoun: these fired ahead of recall and the
|
|
// model, and answered a question about a file or a server with
|
|
// "задач нет."
|
|
"что нужно сделать чтобы перезапустить сервер?",
|
|
"что мне сделать с этим файлом?",
|
|
"what does docker do?",
|
|
"what do you do?",
|
|
"какая погода?",
|
|
"напомни мне позвонить маме в шесть",
|
|
"я сделал зарядку",
|
|
"",
|
|
}
|
|
for _, s := range no {
|
|
if IsTaskListQuery(s) {
|
|
t.Errorf("IsTaskListQuery(%q) = true, want false", s)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestTaskCaptureGrammarClaimsTheMarker — the capture marker is answered at
|
|
// stage 0, so the model never gets to call it an act (Vikunja #467).
|
|
func TestTaskCaptureGrammarClaimsTheMarker(t *testing.T) {
|
|
g := TaskCaptureGrammar()
|
|
captures := map[string]string{
|
|
"добавь в задачи купить молоко": "купить молоко",
|
|
"запиши в список дел купить хлеб": "купить хлеб",
|
|
"поставь задачу вынести мусор": "вынести мусор",
|
|
"добавь в задачи срочно оплатить дом": "оплатить дом",
|
|
}
|
|
for in, want := range captures {
|
|
m := g.Pattern.FindStringSubmatch(in)
|
|
if m == nil {
|
|
t.Fatalf("%q did not match the grammar pattern", in)
|
|
}
|
|
d, ok := g.Build(m)
|
|
if !ok {
|
|
t.Fatalf("%q must be claimed as a capture", in)
|
|
}
|
|
if d.Intent != IntentNote || d.Slots.Text != want {
|
|
t.Errorf("%q → intent=%s text=%q, want note/%q", in, d.Intent, d.Slots.Text, want)
|
|
}
|
|
}
|
|
// Everything without a marker falls through, including a marker with no
|
|
// task after it and a question about the list.
|
|
for _, in := range []string{"надо бы поспать", "добавь в задачи", "какие у меня задачи?", "перезапусти nginx"} {
|
|
if m := g.Pattern.FindStringSubmatch(in); m != nil {
|
|
if _, ok := g.Build(m); ok {
|
|
t.Errorf("%q must fall through to the cascade", in)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestTaskListGrammarClaimsTheAsk — a list question answered before the model,
|
|
// including the phrasings with no possessive that used to route elsewhere.
|
|
func TestTaskListGrammarClaimsTheAsk(t *testing.T) {
|
|
g := TaskListGrammar()
|
|
claimed := []string{"какие у меня задачи", "список дел", "что мне нужно сделать"}
|
|
for _, u := range claimed {
|
|
m := g.Pattern.FindStringSubmatch(u)
|
|
if m == nil {
|
|
t.Fatalf("%q did not match the grammar pattern", u)
|
|
}
|
|
d, ok := g.Build(m)
|
|
if !ok || d.Intent != IntentQuery {
|
|
t.Errorf("%q built %+v ok=%v; want a query", u, d, ok)
|
|
}
|
|
}
|
|
passed := []string{"как дела", "напомни купить хлеб", "что docker делает"}
|
|
for _, u := range passed {
|
|
m := g.Pattern.FindStringSubmatch(u)
|
|
if _, ok := g.Build(m); ok {
|
|
t.Errorf("%q was claimed as a task list", u)
|
|
}
|
|
}
|
|
}
|