a missing slot asks, whatever the confidence (V-557)
The clarify path was gated on dec.Clarify, so a turn the cascade routed confidently but incompletely skipped it. "напомни позвонить" reached applyAction, failed on the missing time and parked nothing, and the "в семь вечера" that followed was routed as a world question and web-searched. The gate now also fires when missingFor names a required slot. A bare capture verb gets a stage-0 rule of its own: it was reaching the resident model as chat, which answered by agreeing to a wording change nobody asked for.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BareCaptureGrammar — a capture verb with nothing after it is a fact she has
|
||||
// yet to hear, not a conversation (Vikunja #557).
|
||||
//
|
||||
// A bare "запиши" reached the resident model as chat, and the model answered by
|
||||
// agreeing to something nobody asked for: "Я поняла, теперь я буду говорить
|
||||
// «записала» или «записала заметку»." It read its own instruction block as the
|
||||
// subject of the turn. Whatever the routing, that reply is invented.
|
||||
//
|
||||
// The route this rule asks for is a fact with no key, which is a gap the clarify
|
||||
// path already has copy for ("Что записать?"). So the rule does not answer the
|
||||
// turn — it hands it to the one mechanism that asks.
|
||||
//
|
||||
// Wired before TaskCaptureGrammar, whose pattern needs an object, so it can
|
||||
// never claim this shape. There is nothing to disambiguate: the whole utterance
|
||||
// is one verb from a closed lexicon.
|
||||
func BareCaptureGrammar() []Grammar {
|
||||
return []Grammar{{
|
||||
Name: "bare-capture",
|
||||
Pattern: bareCapturePattern,
|
||||
Build: bareCaptureBuild,
|
||||
}}
|
||||
}
|
||||
|
||||
// Anchored at both ends, so only the verb and punctuation are in the utterance.
|
||||
// Trailing "-ка" and "пожалуйста" are the same request said politely.
|
||||
var bareCapturePattern = regexp.MustCompile(`(?i)^\s*([\p{L}]+)(?:-ка)?[,\s]*(?:пожалуйста)?[\s.!?]*$`)
|
||||
|
||||
func bareCaptureBuild(m []string) (Decision, bool) {
|
||||
word := strings.ToLower(strings.TrimSpace(m[1]))
|
||||
for _, v := range captureVerbs {
|
||||
if word != v {
|
||||
continue
|
||||
}
|
||||
// No key, no text: she was told to record and not what. Confidence is
|
||||
// 1.0 about the shape, which is all stage 0 ever claims — the gap is
|
||||
// carried by the empty slots, not by a doubt.
|
||||
return Decision{
|
||||
Stage: 0,
|
||||
Intent: IntentFact,
|
||||
Confidence: 1.0,
|
||||
}, true
|
||||
}
|
||||
return Decision{}, false
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package router
|
||||
|
||||
import "testing"
|
||||
|
||||
// TestBareCaptureIsAFactWithNoKey — Vikunja #557. The whole point is the gap:
|
||||
// she must route it as a fact she cannot write yet, so the clarify path asks.
|
||||
func TestBareCaptureIsAFactWithNoKey(t *testing.T) {
|
||||
for _, u := range []string{"запиши", "Запиши.", "запомни, пожалуйста", "отметь!", "note", "запиши-ка"} {
|
||||
m := bareCapturePattern.FindStringSubmatch(u)
|
||||
if m == nil {
|
||||
t.Errorf("%q did not match the shape", u)
|
||||
continue
|
||||
}
|
||||
dec, ok := bareCaptureBuild(m)
|
||||
if !ok {
|
||||
t.Errorf("%q should route as a fact with no key", u)
|
||||
continue
|
||||
}
|
||||
if dec.Intent != IntentFact || dec.Slots.HasKey || dec.Slots.Text != "" {
|
||||
t.Errorf("%q built %+v, want an empty fact", u, dec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestBareCaptureDeclinesAnythingWithAnObject — the object cases belong to the
|
||||
// capture markers and the cascade, and a lone non-capture word is not this rule.
|
||||
func TestBareCaptureDeclinesAnythingWithAnObject(t *testing.T) {
|
||||
for _, u := range []string{"запиши что я пил воду", "добавь задачу купить хлеб", "привет", "вода", "расскажи"} {
|
||||
m := bareCapturePattern.FindStringSubmatch(u)
|
||||
if m == nil {
|
||||
continue // shape already declined it
|
||||
}
|
||||
if _, ok := bareCaptureBuild(m); ok {
|
||||
t.Errorf("%q must not be claimed as a bare capture", u)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user