1c8a32c3fd
Both shapes carry no question mark and no interrogative, so the model saw them with nothing deterministic in front and routed both to fact. The fact gate caught the write and re-ran the turn as a query, so nothing broke — what they cost was a full model round trip for a decision two patterns can make offline. NarrativeQueryGrammars, wired after the agenda rules so that "расскажи, что у меня сегодня" stays an agenda question. Two exclusions, both learned from the fixture: a capture verb in the rest of the utterance means he asked for a note, and an entertainment noun means chat — "расскажи анекдот про программистов" is ru-chat-003, and my first pattern took it. The fixture had no case for either shape, which is why they went unnoticed. Added as ru-query-020 and ru-query-021: classifier+onnx 53/77 → 55/79 (68.8% → 69.6%), both new cases answered at stage 0, false clarifies unchanged at 0.
115 lines
3.8 KiB
Go
115 lines
3.8 KiB
Go
package router
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
)
|
||
|
||
// agendaRouter wires both grammar sets in the order the daemon wires them
|
||
// (voicewire.go): the clock rules first, the agenda rules after, so a test
|
||
// that passes here is a test of the deployed precedence.
|
||
func agendaRouter(t *testing.T) *Router {
|
||
t.Helper()
|
||
r := newTestRouter(t, 0.0)
|
||
r.grammars = append(r.grammars, SystemTimeDateGrammars()...)
|
||
r.grammars = append(r.grammars, AgendaQueryGrammars()...)
|
||
return r
|
||
}
|
||
|
||
// An agenda question is answered from the calendar, which lives in the query
|
||
// chain. Routed to system it reaches replySystem, which has no agenda arm and
|
||
// says "пока не умею" — seen on the deployed daemon, 01-08-2026.
|
||
func TestAgendaQuestionsRouteToQuery(t *testing.T) {
|
||
r := agendaRouter(t)
|
||
for _, u := range []string{
|
||
"что у меня сегодня",
|
||
"что у меня в календаре сегодня",
|
||
"что у меня стоит в календаре на послезавтра",
|
||
"какие у меня встречи завтра",
|
||
"покажи расписание на среду",
|
||
} {
|
||
d, err := r.Route(context.Background(), u, refNow())
|
||
if err != nil {
|
||
t.Fatalf("route(%q): %v", u, err)
|
||
}
|
||
if d.Intent != IntentQuery {
|
||
t.Errorf("route(%q) = %s, want query", u, d.Intent)
|
||
}
|
||
}
|
||
}
|
||
|
||
// The clock rules keep their utterances. They are registered first and the
|
||
// agenda patterns do not match them, so both statements have to hold.
|
||
func TestAgendaGrammarsLeaveTheClockAlone(t *testing.T) {
|
||
r := agendaRouter(t)
|
||
for _, u := range []string{
|
||
"какой сегодня день",
|
||
"какое сегодня число",
|
||
"который час",
|
||
"сколько сейчас времени",
|
||
} {
|
||
d, err := r.Route(context.Background(), u, refNow())
|
||
if err != nil {
|
||
t.Fatalf("route(%q): %v", u, err)
|
||
}
|
||
if d.Intent != IntentSystem {
|
||
t.Errorf("route(%q) = %s, want system", u, d.Intent)
|
||
}
|
||
}
|
||
}
|
||
|
||
// The agenda pattern is anchored and needs the possessive, so an ordinary
|
||
// statement that happens to contain "у меня" is not swallowed.
|
||
func TestAgendaGrammarSparesStatements(t *testing.T) {
|
||
r := agendaRouter(t)
|
||
for _, u := range []string{
|
||
"у меня кончилась вода",
|
||
"напомни мне завтра позвонить маме",
|
||
} {
|
||
d, err := r.Route(context.Background(), u, refNow())
|
||
if err != nil {
|
||
t.Fatalf("route(%q): %v", u, err)
|
||
}
|
||
if d.Stage == 0 && d.Intent == IntentQuery {
|
||
t.Errorf("route(%q) was claimed by the agenda grammar", u)
|
||
}
|
||
}
|
||
}
|
||
|
||
// The two shapes that carried no question mark and no interrogative, so the
|
||
// model saw them first and called them facts (Vikunja #498).
|
||
func TestNarrativeGrammarsRouteToQuery(t *testing.T) {
|
||
r := agendaRouter(t)
|
||
r.grammars = append(r.grammars, NarrativeQueryGrammars()...)
|
||
for _, u := range []string{
|
||
"что дальше?",
|
||
"и что там дальше",
|
||
"what's next?",
|
||
"расскажи про битву при Ватерлоо",
|
||
"объясни как работает дизель",
|
||
"опиши Ватерлоо",
|
||
} {
|
||
d, err := r.Route(context.Background(), u, refNow())
|
||
if err != nil {
|
||
t.Fatalf("%q: %v", u, err)
|
||
}
|
||
if d.Intent != IntentQuery || d.Stage != 0 {
|
||
t.Errorf("%q routed intent=%s stage=%d, want query at stage 0", u, d.Intent, d.Stage)
|
||
}
|
||
}
|
||
}
|
||
|
||
// A narrative verb next to a capture verb is him asking for a note. Stage 0
|
||
// declines and the extractor gets its turn.
|
||
func TestNarrativeGrammarLeavesCapturesAlone(t *testing.T) {
|
||
r := agendaRouter(t)
|
||
r.grammars = append(r.grammars, NarrativeQueryGrammars()...)
|
||
d, err := r.Route(context.Background(), "расскажи и запиши что я пил воду", refNow())
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if d.Stage == 0 && d.Intent == IntentQuery {
|
||
t.Errorf("stage 0 claimed a capture: %+v", d)
|
||
}
|
||
}
|