Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions backend/tests/domainClassifier.prefixKeywords.unit.test.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
8 changes: 7 additions & 1 deletion backend/utils/domainClassifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down