package router import "time" // Habit queries — "что я обычно делаю по вторникам?" (Vikunja #254). // // Deterministic matching, like the calendar and plan matchers: the LLM router // classifies the intent, but WHICH weekday was asked about is a lookup, not a // generation. A model that answers "по вторникам" for a question about Thursday // gives a confidently wrong account of the owner's own life. // HabitQuery — a parsed "what do I usually do" question. Weekday is set only // when the utterance names one; otherwise the answer covers the whole week. // Weekend is set for "по выходным", which names two days rather than one. type HabitQuery struct { Weekday time.Weekday HasWeekday bool Weekend bool } // habitMarkers — the words that make a question about habit rather than about // today. Without one of these, "что я делаю" is a question about right now, and // the recall path owns it. var habitMarkers = []string{ "обычно", "обычное", "чаще", "постоянно", "привычки", "привычка", "привычках", "регулярно", "каждый", "каждую", "каждое", "usually", "habits", "habit", "typically", "normally", } // The weekday a habit question names comes from WeekdayIndex, not from a map // here. This file used to keep its own declension table, which had "воскресеньях" // and no "средах" — a list of forms is finished by whoever last thought of one, // and a dictionary is not (V-581). // weekendWords — the weekend as one unit. "что я обычно делаю по выходным?" // has a habit marker and names days, but no weekday name is in it, so it used // to fall through to the whole-week profile and answer about Tuesdays too. var weekendWords = map[string]bool{ "выходным": true, "выходные": true, "выходных": true, "выходной": true, "weekend": true, "weekends": true, } // ParseHabitQuery reports whether an utterance asks what the owner usually // does, and on which weekday if it names one. // // A habit marker is required. "что я делаю в среду?" without one is a question // about this coming Wednesday — the calendar's job — and answering it with a // statistical average would be answering a different question. func ParseHabitQuery(text string) (HabitQuery, bool) { toks := planTokens(text) marked := false for _, t := range toks { for _, m := range habitMarkers { if t == m { marked = true break } } } if !marked { return HabitQuery{}, false } for _, t := range toks { if wd, ok := WeekdayIndex(t); ok { return HabitQuery{Weekday: wd, HasWeekday: true}, true } if weekendWords[t] { return HabitQuery{Weekend: true}, true } } return HabitQuery{}, true }