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:
+5
-2
@@ -13,8 +13,11 @@ certs/
|
||||
# Dependencies (fetch/build, not vendored)
|
||||
deps/
|
||||
|
||||
# ML models (large, downloaded separately)
|
||||
models/
|
||||
# ML models (large, downloaded separately) — specific dirs, not blanket,
|
||||
# because models/seeds/*.txt are small, tracked files the classifier needs.
|
||||
/models/embedder/
|
||||
/models/stt/
|
||||
/models/tts/
|
||||
|
||||
# Runtime data
|
||||
*.db
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
maven, restart nginx
|
||||
maven, stop the backup
|
||||
maven, start the deployment
|
||||
maven, run the backup script
|
||||
maven, turn on the heater
|
||||
maven, выключи свет
|
||||
maven, запусти обновление
|
||||
maven, открой шлагбаум
|
||||
maven, сделай бэкап
|
||||
maven, выключи сервер
|
||||
включи микроволновку
|
||||
выключи кондиционер
|
||||
включи свет на кухне
|
||||
открой ворота
|
||||
запусти вентиляцию
|
||||
выключи телевизор
|
||||
включи музыку
|
||||
выключи музыку
|
||||
сделай громче
|
||||
сделай тише
|
||||
перезагрузи роутер
|
||||
включи кофеварку
|
||||
открой окно
|
||||
закрой окно
|
||||
запусти пылесос
|
||||
останови пылесос
|
||||
включи обогреватель
|
||||
выключи обогреватель
|
||||
включи чайник
|
||||
открой шторы
|
||||
закрой шторы
|
||||
включи полив
|
||||
выключи полив
|
||||
заблокируй дверь
|
||||
открой дверь
|
||||
запусти обновление системы
|
||||
останови сервис
|
||||
перезапусти медиацентр
|
||||
включи кондиционер
|
||||
поставь таймер на 10 минут
|
||||
выключи всё
|
||||
перезагрузи компьютер
|
||||
разблокируй дверь
|
||||
запусти диагностику
|
||||
останови все
|
||||
# --- homelab read-only queries ---
|
||||
покажи статус nginx
|
||||
статус докера
|
||||
покажи запущенные контейнеры
|
||||
сколько места на диске
|
||||
свободная память
|
||||
покажи uptime
|
||||
покажи логи nginx
|
||||
лог докера
|
||||
статус sshd
|
||||
как работает сервер
|
||||
что запущено
|
||||
|
||||
# --- homelab destructive (with confirm gate) ---
|
||||
перезапусти nginx
|
||||
перезагрузи докер
|
||||
останови nginx
|
||||
запусти nginx
|
||||
перезагрузи сервер
|
||||
останови контейнер
|
||||
перезапусти сервис
|
||||
останови сервис
|
||||
запусти сервис
|
||||
перезагрузи систему
|
||||
@@ -0,0 +1,40 @@
|
||||
отметь что я выпил воды
|
||||
slept 6h
|
||||
запиши что я пообедал
|
||||
отметь тренировку 45 минут
|
||||
записываю вес 72 килограмма
|
||||
отметь что покормил кота
|
||||
принял лекарство
|
||||
выпил кофе
|
||||
отметь что я сделал зарядку
|
||||
logged 8 hours of sleep
|
||||
запиши что съел на завтрак
|
||||
отметь температуру 36.6
|
||||
записываю давление 120 на 80
|
||||
отметь пробежку 5 километров
|
||||
выпил 2 литра воды
|
||||
записываю что почистил зубы
|
||||
отметь приём витаминов
|
||||
запиши обед: гречка с курицей
|
||||
отметь что полил цветы
|
||||
вес 73.5 килограмма
|
||||
тренировка 30 минут
|
||||
сон 7 часов
|
||||
выпил таблетку от головы
|
||||
пообедал в 2 часа
|
||||
выпил стакан сока
|
||||
записываю что сходил в душ
|
||||
отметь что переоделся
|
||||
вечерний приём лекарств
|
||||
заряжался 15 минут
|
||||
записываю утреннюю пробежку
|
||||
accounted 2000 ml of water
|
||||
weight 71.8 kg
|
||||
ran 3 kilometers
|
||||
meditation 10 minutes
|
||||
breakfast: oatmeal with berries
|
||||
took vitamin d
|
||||
walked 8000 steps
|
||||
stretching 20 minutes
|
||||
logged blood sugar 5.2
|
||||
dinner at 7pm
|
||||
@@ -0,0 +1,34 @@
|
||||
note: staggered cooldown by time of day
|
||||
заметка: купить новый фильтр для воды
|
||||
запиши идею про автоматизацию полива
|
||||
note: check the DNS config after update
|
||||
заметка: починить ручку на двери
|
||||
запиши рецепт: 3 яйца, мука, молоко
|
||||
заметка: сменить масло в машине
|
||||
note: upgrade wireguard on the phone
|
||||
запиши: купить подарок на день рождения
|
||||
заметка: заменить лампочку в коридоре
|
||||
напомни себе купить новый жесткий диск
|
||||
запиши: проверить проводку на даче
|
||||
заметка: переустановить home assistant
|
||||
note: add a backup job for the database
|
||||
запиши пароль от wifi в заметки
|
||||
заметка: починить протечку крана
|
||||
купить новый фильтр для аквариума
|
||||
заметка: записаться к стоматологу
|
||||
запиши рецепт блинов
|
||||
заметка: посадить рассаду в марте
|
||||
запиши адрес: москва, тверская 7
|
||||
заметка: просмотреть документацию по API
|
||||
запиши: купить семена для огорода
|
||||
заметка: обновить прошивку роутера
|
||||
запиши книгу, которую посоветовали
|
||||
заметка: сделать прививку от гриппа
|
||||
запиши цену на стройматериалы
|
||||
заметка: проверить уровень масла
|
||||
запиши размеры полки для шкафа
|
||||
заметка: заказать запчасти для стиралки
|
||||
note: ideas for the garden automation
|
||||
запиши что нужно купить в магазине
|
||||
заметка: переклеить обои в спальне
|
||||
запиши время работы химчистки
|
||||
@@ -35,7 +35,8 @@ how many hours did I sleep this week
|
||||
покажи логи за сегодня
|
||||
кто последний раз делал бэкап
|
||||
какой ip адрес у сервера
|
||||
сколько стоит свет в этом месяценайди заметку про сервер
|
||||
сколько стоит свет в этом месяце
|
||||
найди заметку про сервер
|
||||
найди мою заметку о бэкапах
|
||||
поищи заметку про роутер
|
||||
найди заметку где я записал пароль
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
напомни через 4 часа размяться
|
||||
remind me in 30 minutes to drink water
|
||||
напомни завтра в 9 утра позвонить
|
||||
напомни через час проверить духовку
|
||||
напомни в пятницу вынести мусор
|
||||
remind me at 6pm to take out the trash
|
||||
напомни через 15 минут снять бельё
|
||||
напомни в 8 вечера встречу
|
||||
напомни через 2 дня оплатить счёт
|
||||
напомни завтра утром покормить кота
|
||||
remind me tomorrow at 8am to call the doctor
|
||||
напомни через 10 минут снять с огня
|
||||
напомни в среду оплатить интернет
|
||||
напомни сегодня в 3 часа собрание
|
||||
remind me next monday to submit the report
|
||||
напомни через 3 часа принять лекарство
|
||||
напомни завтра вечером купить хлеб
|
||||
remind me in 5 minutes to check the oven
|
||||
напомни в понедельник утром вынести мусор
|
||||
напомни через 30 минут выключить чайник
|
||||
напомни сегодня вечером позвонить маме
|
||||
remind me on friday at noon to water plants
|
||||
напомни через неделю заплатить за квартиру
|
||||
напомни завтра в 10 утра тренировка
|
||||
напомни в воскресенье убраться в гараже
|
||||
напомни через 2 часа покормить собаку
|
||||
напомни сегодня в полдень обед
|
||||
напомни в 7 утра зарядка
|
||||
напомни через минуту снять пробу
|
||||
напомни завтра в 5 вечера забрать посылку
|
||||
remind me tonight at 10pm to take medicine
|
||||
напомни в четверг заплатить налоги
|
||||
напомни через 20 минут выключить духовку
|
||||
напомни в субботу убрать в комнате
|
||||
напомни завтра после обеда позвонить
|
||||
# --- pure-verb reminder seeds (no time expression) — shifts centroid away
|
||||
# from time-lexicon dominance toward the reminding intent itself.
|
||||
напомни мне
|
||||
напомни пожалуйста
|
||||
не забудь напомнить
|
||||
поставь напоминание
|
||||
создай напоминание
|
||||
добавь напоминание
|
||||
запиши напоминание
|
||||
set a reminder
|
||||
create a reminder
|
||||
make a reminder
|
||||
@@ -0,0 +1,51 @@
|
||||
который час
|
||||
сколько времени
|
||||
какой сегодня день
|
||||
какое сегодня число
|
||||
который час в Москве
|
||||
сколько сейчас времени
|
||||
который час у нас
|
||||
сколько времени осталось до вечера
|
||||
какой сегодня день недели
|
||||
какая температура воздуха
|
||||
сколько человек дома
|
||||
кто сейчас дома
|
||||
есть ли кто дома
|
||||
кто дома сейчас
|
||||
все ли дома
|
||||
сколько памяти занято
|
||||
какая загрузка процессора
|
||||
сколько свободного места на диске
|
||||
какой ip адрес у сервера
|
||||
как дела у сервера
|
||||
всё ли работает
|
||||
сколько сервер работает без перезагрузки
|
||||
когда сервер запускался
|
||||
какая версия софта
|
||||
сколько аптайм
|
||||
какой статус сервисов
|
||||
все ли сервисы работают
|
||||
что с интернетом
|
||||
интернет работает
|
||||
когда последний раз перезагружался
|
||||
сколько трафика сегодня
|
||||
какая скорость интернета
|
||||
загрузка сети
|
||||
сколько процессов запущено
|
||||
как загрузка системы
|
||||
сколько оперативной памяти свободно
|
||||
какая температура процессора
|
||||
тихий режим
|
||||
тихо
|
||||
не шуми
|
||||
тихий режим включи
|
||||
включи тихий режим
|
||||
не беспокоить
|
||||
выключи тихий режим
|
||||
шумный режим
|
||||
громкий режим
|
||||
отключи тихий режим
|
||||
quiet mode on
|
||||
quiet mode off
|
||||
quiet on
|
||||
quiet off
|
||||
Reference in New Issue
Block a user