MediBot / lib /rag /query.ts
github-actions[bot]
Deploy MedOS Global from 956ae78f
e3e3e14
Raw
History Blame Contribute Delete
1.93 kB
/**
* Query preprocessing for retrieval.
*
* - normalizeQuery: trim, collapse whitespace, cap length.
* - buildFtsMatch: turn free user text into a SAFE FTS5 MATCH expression.
* Passing raw user input to FTS5 MATCH throws on punctuation/operators
* ("?", "-", quotes, AND/OR/NEAR), so we tokenise, drop noise, quote
* each term, and OR them together.
*
* Kept deterministic and dependency-free. An optional LLM-based rewrite
* can be layered on later behind its own flag; it is intentionally not in
* the hot path here (one fewer model call per turn).
*/
const MAX_LEN = 500;
// Small, language-agnostic-ish stop list. Retrieval is robust to a few
// extra terms, so this only needs to drop the highest-frequency noise.
const STOP = new Set([
'the', 'and', 'for', 'are', 'but', 'not', 'you', 'your', 'with', 'have',
'has', 'had', 'this', 'that', 'what', 'why', 'how', 'when', 'who', 'can',
'could', 'would', 'should', 'about', 'from', 'into', 'than', 'then',
'them', 'they', 'there', 'here', 'been', 'was', 'were', 'will', 'just',
'get', 'got', 'feel', 'feeling', 'please', 'help', 'tell', 'know',
]);
export function normalizeQuery(raw: string): string {
return (raw || '').replace(/\s+/g, ' ').trim().slice(0, MAX_LEN);
}
/** Extract content tokens (lowercased, >=3 chars, non-stopword). */
export function queryTerms(query: string): string[] {
const terms = (query.toLowerCase().match(/[a-z0-9]+/g) || [])
.filter((t) => t.length >= 3 && !STOP.has(t));
return Array.from(new Set(terms)).slice(0, 24);
}
/**
* Build a safe FTS5 MATCH string, e.g. `"chest" OR "pain" OR "breath"`.
* Returns '' when there are no usable terms (caller then skips keyword
* search and relies on vector similarity).
*/
export function buildFtsMatch(query: string): string {
const terms = queryTerms(query);
if (terms.length === 0) return '';
return terms.map((t) => `"${t}"`).join(' OR ');
}