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:
kami
2026-07-06 18:38:48 +04:00
parent ce3d8e65f2
commit bf4009ca4f
9 changed files with 361 additions and 3 deletions
+30
View File
@@ -712,6 +712,13 @@ func (h *reactiveHandler) replySystem(ctx context.Context, dec router.Decision)
u := strings.ToLower(dec.Utterance)
now := h.now()
// stage-0 grammars catch the exact time/date patterns, but duration
// queries ("сколько времени прошло") bypass the grammar's build filter
// and can reach replySystem via the classifier path. Guard against them.
if hasDurationWords(u) {
return "пока не умею отвечать на этот вопрос."
}
switch {
case strings.Contains(u, "час") || strings.Contains(u, "врем"):
h := now.Hour()
@@ -735,6 +742,29 @@ func (h *reactiveHandler) replySystem(ctx context.Context, dec router.Decision)
}
}
// hasDurationWords checks whether u is asking about elapsed/remaining time
// rather than the current clock — guards replySystem from replying "сейчас
// X часов" to "сколько времени прошло". Mirrors the stage0.go build filter.
func hasDurationWords(u string) bool {
s := strings.ToLower(strings.TrimSpace(u))
// First-word duration markers (same keywords as timeQueryBuild in stage0).
first := strings.Fields(s)
if len(first) > 0 {
switch first[0] {
case "прошло", "осталось", "пройдет", "минуло", "проходит":
return true
}
}
// Broader duration keywords appearing anywhere in the utterance.
if strings.Contains(s, "прошло") || strings.Contains(s, "осталось") {
return true
}
if strings.Contains(s, " до ") {
return true
}
return false
}
// reply wraps a text reply through TTS to produce a PushToTalkResp. If TTS
// fails, the response carries an empty audio + the text — the client can
// still display text if it can't play. The routedChannels field is