From 1d8990590e52067d7ae0103cef2ca040c148dc2e Mon Sep 17 00:00:00 2001 From: ionfwsrijan Date: Fri, 7 Aug 2026 01:03:47 +0530 Subject: [PATCH] fix: route prefix and non-word-character prep topics to the AI mentor The keyword regex used word boundaries on both ends, so prefix keywords (scal, load balanc, negotiat, probab, math, quant) and keywords containing non-word characters (c++, c#) never matched. 'Explain probability', 'What is load balancing?', 'Explain C++ pointers', etc. were misclassified as off-topic and refused. Anchor keywords as prefixes by dropping the trailing word boundary. Closes #1440 --- ...mainClassifier.prefixKeywords.unit.test.js | 39 +++++++++++++++++++ backend/utils/domainClassifier.js | 8 +++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 backend/tests/domainClassifier.prefixKeywords.unit.test.js diff --git a/backend/tests/domainClassifier.prefixKeywords.unit.test.js b/backend/tests/domainClassifier.prefixKeywords.unit.test.js new file mode 100644 index 00000000..bddcc6ef --- /dev/null +++ b/backend/tests/domainClassifier.prefixKeywords.unit.test.js @@ -0,0 +1,39 @@ +import { describe, it, expect } from "vitest"; +import { isPrepPilotDomain } from "../utils/domainClassifier.js"; + +// --------------------------------------------------------------------------- +// Domain classifier prefix-keyword fix (issue #1440): core prep topics that +// are prefixes of real words (probab -> probability, scal -> scaling, math -> +// mathematics, load balanc -> load balancing, negotiat -> negotiation) and +// keywords containing non-word characters (c++, c#) previously never matched +// because of the trailing \b in the keyword regex, so the AI mentor refused +// legitimate questions about them. +// --------------------------------------------------------------------------- + +describe("isPrepPilotDomain — prefix and non-word-character keywords", () => { + const cases = [ + "probability", + "Explain probability.", + "What is load balancing?", + "How do I scale my database?", + "scaling", + "Explain C++ pointers.", + "Explain C# async/await.", + "C++", + "C#", + "negotiation", + "mathematics", + "quantitative reasoning" + ]; + + cases.forEach((query) => { + it(`routes "${query}" to the AI mentor`, () => { + expect(isPrepPilotDomain(query)).toBe(true); + }); + }); + + it("still rejects clearly off-topic prompts", () => { + expect(isPrepPilotDomain("Who won the FIFA World Cup?")).toBe(false); + expect(isPrepPilotDomain("Write a recipe for pasta.")).toBe(false); + }); +}); diff --git a/backend/utils/domainClassifier.js b/backend/utils/domainClassifier.js index f132d514..976ed79e 100644 --- a/backend/utils/domainClassifier.js +++ b/backend/utils/domainClassifier.js @@ -36,7 +36,13 @@ const escapeRegExp = (string) => { }; const escapedKeywords = domainKeywords.map(escapeRegExp); -const keywordRegex = new RegExp(`\\b(${escapedKeywords.join('|')})\\b`, 'i'); +// Anchor keywords as prefixes: keep the leading word boundary but drop the +// trailing one. A trailing \b made prefix keywords (scal, load balanc, +// negotiat, probab, math, quant) and keywords ending in non-word characters +// (c++, c#) never match — "probability", "load balancing", "C++?" etc. were +// misclassified as off-topic. Prefix matching over-routes a few extra words +// (e.g. "google" matching "go"), which is acceptable for an AI mentor router. +const keywordRegex = new RegExp(`\\b(${escapedKeywords.join('|')})`, 'i'); const conversationalRegex = /^(hi|hello|hey|yo|ok|thanks|thank you|who are you|what should i call you|good morning|good evening|\?)$/i;