history questions match verbs by lemma (V-530)
historyMarkers were truncated Russian prefixes, so "что я читал рассказ" read as a history question because "рассказ" is a prefix of "рассказывал". That is the defect V-528 fixed in complaint.go, where "лаг" matched "лагерь". A history question is now an interrogative, plus a first- or second-person subject, plus a verb of saying or recording matched through morph.SameWord. "что записать?" is a verb with nobody saying it and no longer claims the turn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+76
-13
@@ -6,6 +6,8 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kami/maven/internal/morph"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command history — "что я тебе говорил?", "что ты записала сегодня?"
|
// Command history — "что я тебе говорил?", "что ты записала сегодня?"
|
||||||
@@ -15,18 +17,34 @@ import (
|
|||||||
// storage: everything he tapped in is already a row with a source and a
|
// storage: everything he tapped in is already a row with a source and a
|
||||||
// timestamp, and this only reads them back.
|
// timestamp, and this only reads them back.
|
||||||
|
|
||||||
// historyMarkers — the ways he asks what he told her. Each entry is a pair of
|
// A history question needs three things in one utterance: the interrogative,
|
||||||
// substrings that must BOTH appear, because either half alone is a different
|
// whose turn is being asked about, and a verb of saying or recording. Any two of
|
||||||
// question: "что я говорил про сервер" is a recall question the notes pass
|
// them are a different question. "что я говорил про сервер" names a topic and
|
||||||
// answers better, and "что ты записала" with no "что" is not a question at all.
|
// the notes pass answers it better; "записал молоко" is a capture.
|
||||||
var historyMarkers = [][2]string{
|
//
|
||||||
{"что я", "говорил"},
|
// The verbs are matched by lemma through internal/morph, not by a truncated
|
||||||
{"что я", "сказал"},
|
// prefix (Vikunja #530). The pairs here used to hold "рассказ" and "записал",
|
||||||
{"что я", "рассказ"},
|
// which is the defect V-528 fixed in complaint.go: "рассказ" is also the noun,
|
||||||
{"что ты", "записал"},
|
// so "что я рассказал ей" and "что я читал рассказ" were the same string test.
|
||||||
{"что ты", "запомнил"},
|
// Aspect pairs are separate lemmas in the dictionary, so both members are listed.
|
||||||
{"что я", "отмечал"},
|
var (
|
||||||
{"что я", "отметил"},
|
// historySpokenVerbs — what HE did. "что я тебе говорил".
|
||||||
|
historySpokenVerbs = []string{"говорить", "сказать", "рассказать", "рассказывать", "отметить", "отмечать"}
|
||||||
|
|
||||||
|
// historyRecordedVerbs — what SHE did with it. "что ты записала сегодня".
|
||||||
|
historyRecordedVerbs = []string{"записать", "запомнить", "отметить", "отмечать"}
|
||||||
|
|
||||||
|
// firstPersonSubjects and secondPersonSubjects — whose turn the question is
|
||||||
|
// about. Only the subject forms: "что я тебе говорил" is his turn, and the
|
||||||
|
// dative "тебе" in it is not the subject.
|
||||||
|
firstPersonSubjects = []string{"я"}
|
||||||
|
secondPersonSubjects = []string{"ты"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// historyMarkersEn — the English pairs, kept as substrings because the
|
||||||
|
// dictionary is Russian. Each half alone is a different question, the same way
|
||||||
|
// the Russian test needs all three parts.
|
||||||
|
var historyMarkersEn = [][2]string{
|
||||||
{"what did i", "tell"},
|
{"what did i", "tell"},
|
||||||
{"what did you", "record"},
|
{"what did you", "record"},
|
||||||
}
|
}
|
||||||
@@ -47,11 +65,56 @@ func isHistoryQuery(u string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, pair := range historyMarkers {
|
for _, pair := range historyMarkersEn {
|
||||||
if strings.Contains(s, pair[0]) && strings.Contains(s, pair[1]) {
|
if strings.Contains(s, pair[0]) && strings.Contains(s, pair[1]) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
toks := historyTokens(s)
|
||||||
|
if !hasAny(toks, "что", "чего") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if hasAny(toks, firstPersonSubjects...) && hasVerbForm(toks, historySpokenVerbs) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return hasAny(toks, secondPersonSubjects...) && hasVerbForm(toks, historyRecordedVerbs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// historyTokens splits an utterance into bare words. The punctuation goes
|
||||||
|
// because "говорил?" is the same word as "говорил".
|
||||||
|
func historyTokens(s string) []string {
|
||||||
|
toks := strings.Fields(s)
|
||||||
|
out := make([]string, 0, len(toks))
|
||||||
|
for _, t := range toks {
|
||||||
|
if t = strings.Trim(t, ".,!?;:—–-()\"'«»"); t != "" {
|
||||||
|
out = append(out, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasAny(toks []string, want ...string) bool {
|
||||||
|
for _, t := range toks {
|
||||||
|
for _, w := range want {
|
||||||
|
if t == w {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasVerbForm reports whether any token is a form of any of the lemmas. Both
|
||||||
|
// sides go through the dictionary, so a caller may name the infinitive and he
|
||||||
|
// may say the past tense.
|
||||||
|
func hasVerbForm(toks []string, lemmas []string) bool {
|
||||||
|
for _, t := range toks {
|
||||||
|
for _, l := range lemmas {
|
||||||
|
if morph.SameWord(t, l) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,17 @@ func TestIsHistoryQuery(t *testing.T) {
|
|||||||
{"что я тебе говорил?", true},
|
{"что я тебе говорил?", true},
|
||||||
{"что ты записала сегодня?", true},
|
{"что ты записала сегодня?", true},
|
||||||
{"что я отмечал?", true},
|
{"что я отмечал?", true},
|
||||||
|
// Forms the truncated prefixes did not reach. The dictionary answers
|
||||||
|
// these because it lemmatises both sides (V-530).
|
||||||
|
{"что я тебе рассказывал?", true},
|
||||||
|
{"что я сказала вчера", true},
|
||||||
|
{"что ты запомнила?", true},
|
||||||
|
// The noun, not the verb. "рассказ" was a prefix of the old pair, so
|
||||||
|
// this read as a history question — the same defect V-528 fixed in
|
||||||
|
// complaint.go, where "лаг" matched "лагерь".
|
||||||
|
{"что я читал рассказ", false},
|
||||||
|
// A verb of saying with nobody saying it.
|
||||||
|
{"что записать?", false},
|
||||||
// A named topic is a recall question, and the notes pass answers it
|
// A named topic is a recall question, and the notes pass answers it
|
||||||
// better than a list of the last five facts does.
|
// better than a list of the last five facts does.
|
||||||
{"что я говорил про сервер?", false},
|
{"что я говорил про сервер?", false},
|
||||||
|
|||||||
Reference in New Issue
Block a user