voice/seeds: track seed files, add russian time parser, guard replySystem
five fixes spotted during routing investigation:
- gitignore: replace blanket models/ ignore with per-dir exceptions
(/models/embedder/, /models/stt/, /models/tts/) so the seed text
files under models/seeds/ are tracked in version control
- query.txt: fix merged line — 'сколько стоит свет в этом месяце' and
'найди заметку про сервер' were fused with no separator
- reminder.txt: add 11 pure-verb reminder seeds without time expressions
to shift centroid toward the reminding intent rather than time-lexicon
- StubDateTimeParser: add Russian 'через <N> <unit>'/'через час'/'через
полчаса', 'сегодня'/'завтра'/'послезавтра' with optional clock, and
'в <clock>' scan. Add Russian word numbers (один-десять) and unit
inflections (час/часа/часов, минута/минуты/минут, день/дня/дней,
неделя/недели/недель). Also adds missing English day/week units.
- replySystem: guard time branch against duration queries ('сколько
времени прошло') reaching it via the classifier path after the
stage-0 grammar's build filter rejects them. Mirrors stage0.go
duration keywords.
This commit is contained in:
@@ -198,6 +198,72 @@ func (StubDateTimeParser) Parse(_ context.Context, text string, now time.Time) (
|
||||
return t, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// --- Russian time expressions (stub floor; dateparser replaces) ---
|
||||
|
||||
// "в <clock>" anywhere — mirror of the English "at" scan.
|
||||
for i := 0; i+1 < len(toks); i++ {
|
||||
if toks[i] != "в" {
|
||||
continue
|
||||
}
|
||||
if t, ok := parseClock(toks[i+1], now); ok {
|
||||
return t, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// "через <N> <unit>" / "через <unit>" (bare = 1) / "через полчаса".
|
||||
for i := 0; i+1 < len(toks); i++ {
|
||||
if toks[i] != "через" {
|
||||
continue
|
||||
}
|
||||
// "через N unit" — three-token scan.
|
||||
if i+2 < len(toks) {
|
||||
n, unit, ok := splitNumUnit(toks[i+1] + " " + toks[i+2])
|
||||
if ok {
|
||||
if d, ok := unitToDuration(n, unit); ok {
|
||||
return now.Add(d), true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// "через полчаса"
|
||||
if toks[i+1] == "полчаса" {
|
||||
return now.Add(30 * time.Minute), true, nil
|
||||
}
|
||||
// "через unit" (bare unit without number = 1, e.g. "через час")
|
||||
if d, ok := unitToDuration(1, toks[i+1]); ok {
|
||||
return now.Add(d), true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Calendar day: "сегодня", "завтра", "послезавтра" [в] <clock>
|
||||
for i := 0; i < len(toks); i++ {
|
||||
var dayShift time.Duration
|
||||
switch toks[i] {
|
||||
case "сегодня":
|
||||
dayShift = 0
|
||||
case "завтра":
|
||||
dayShift = 24 * time.Hour
|
||||
case "послезавтра":
|
||||
dayShift = 48 * time.Hour
|
||||
default:
|
||||
continue
|
||||
}
|
||||
base := now.Truncate(24 * time.Hour).Add(dayShift)
|
||||
// Look for clock after the day word (optional "в").
|
||||
nextIdx := i + 1
|
||||
if nextIdx < len(toks) && toks[nextIdx] == "в" {
|
||||
nextIdx++
|
||||
}
|
||||
if nextIdx < len(toks) {
|
||||
if t, ok := parseClock(toks[nextIdx], now); ok {
|
||||
t = time.Date(base.Year(), base.Month(), base.Day(), t.Hour(), t.Minute(), 0, 0, now.Location())
|
||||
return t, true, nil
|
||||
}
|
||||
}
|
||||
// No clock — return midnight of that day.
|
||||
return base, true, nil
|
||||
}
|
||||
|
||||
// bare clock at start ("7:30").
|
||||
if len(toks) > 0 {
|
||||
if t, ok := parseClock(toks[0], now); ok {
|
||||
@@ -286,6 +352,11 @@ var wordNumbers = map[string]int{
|
||||
"six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10,
|
||||
"eleven": 11, "twelve": 12, "fifteen": 15, "twenty": 20,
|
||||
"thirty": 30, "forty": 40, "fifty": 50, "sixty": 60,
|
||||
// Russian word numbers (gender variants cover natural речи)
|
||||
"один": 1, "одна": 1, "одно": 1,
|
||||
"два": 2, "две": 2, "три": 3, "четыре": 4,
|
||||
"пять": 5, "шесть": 6, "семь": 7, "восемь": 8,
|
||||
"девять": 9, "десять": 10,
|
||||
}
|
||||
|
||||
func leadingWordNumber(s string) (int, string, bool) {
|
||||
@@ -308,6 +379,18 @@ func unitToDuration(n int, unit string) (time.Duration, bool) {
|
||||
return time.Duration(n) * time.Minute, true
|
||||
case "s", "sec", "secs", "second", "seconds":
|
||||
return time.Duration(n) * time.Second, true
|
||||
// English day/week (pre-existing gap)
|
||||
case "day", "days":
|
||||
return time.Duration(n) * 24 * time.Hour, true
|
||||
// Russian units (inflected forms)
|
||||
case "час", "часа", "часов":
|
||||
return time.Duration(n) * time.Hour, true
|
||||
case "минута", "минуты", "минут":
|
||||
return time.Duration(n) * time.Minute, true
|
||||
case "день", "дня", "дней":
|
||||
return time.Duration(n) * 24 * time.Hour, true
|
||||
case "неделя", "недели", "недель":
|
||||
return time.Duration(n) * 7 * 24 * time.Hour, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user