package router import ( "strings" "github.com/kami/maven/internal/lexicon" ) // The three word sets this file tests against are closed classes, so they live // complete in internal/lexicon rather than inline here (Vikunja #525). The // inline lists were short: no "чем", no "чём", no "кем", no declined "какой", // so "чем ты занята" carried no interrogative at all and read as a statement. // // interrogatives mark an utterance as asking rather than telling. // narrativeRequests are "tell me about X", which asks for knowledge Maven does // not hold about him and carries neither a question mark nor an interrogative — // that is how "расскажи про битву при Ватерлоо" reached the fact store (#470). // captureVerbs win over both, because "запиши что я пил воду" contains an // interrogative and is still a capture: the word he said is "запиши". // // All three are matched over tokens, never as substrings: "что" inside "чтобы" // and "как" inside "какао" are not questions. var ( interrogatives = lexicon.Interrogatives() narrativeRequests = lexicon.NarrativeRequests() captureVerbs = lexicon.CaptureVerbs() ) // IsQuestionShaped reports whether text asks for something rather than // records it. It is a deterministic offline test over tokens, so it costs // nothing and never depends on the model that produced the routing decision. // // It exists because a mis-routed question used to be persisted as a fact // about the owner, with the model's invented answer as the value (#470). The // predicate is deliberately blunt: refusing to store a question is cheap and // reversible, storing an invented fact about him is neither. func IsQuestionShaped(text string) bool { t := strings.TrimSpace(text) if t == "" { return false } toks := planTokens(t) for _, v := range captureVerbs { if hasTok(toks, v) { return false } } if strings.HasSuffix(t, "?") { return true } for _, w := range interrogatives { if hasTok(toks, w) { return true } } for _, w := range narrativeRequests { if hasTok(toks, w) { return true } } return false }