Project Files
dist / retrieval / queryExpansion.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.expandQueries = expandQueries;
// Maximum number of expansion results to keep in memory. Old entries are
// evicted by insertion order (FIFO approximation) once this cap is reached.
const MAX_EXPANSION_CACHE = 512;
const expansionCache = new Map();
function normalizeQuery(query) {
return query.trim().replace(/\s+/g, " ");
}
function splitClauses(query) {
return query
.split(/[,;\/]|\b(?:and|or|vs|versus)\b/gi)
.map(part => part.trim())
.filter(Boolean);
}
function unique(values) {
const seen = new Set();
const out = [];
for (const value of values) {
const key = value.toLowerCase();
if (seen.has(key))
continue;
seen.add(key);
out.push(value);
}
return out;
}
function expandQueries(query, maxCount) {
const base = normalizeQuery(query);
const cacheKey = `${base}\u0000${maxCount}`;
const cached = expansionCache.get(cacheKey);
if (cached)
return cached;
const words = base.split(/\W+/).filter(Boolean);
const focus = words.slice(0, 6).join(" ");
const clauseVariants = splitClauses(base).slice(0, 2);
const variants = unique([
base,
words.length > 5 ? words.slice(0, 5).join(" ") : "",
focus && focus !== base ? focus : "",
clauseVariants[0] ? `details about ${clauseVariants[0]}` : "",
clauseVariants[1] ? `details about ${clauseVariants[1]}` : "",
words.length > 3 ? `technical details about ${base}` : "",
].filter(Boolean));
const limited = variants.slice(0, Math.max(1, maxCount));
// ── Loop prevention: guarantee non-empty query list ───────────────────────
// retrieveAcrossQueries maps over this array; an empty array means
// Promise.all([]) resolves instantly with no results, then the cache is
// populated with an empty array, and every subsequent call for the same
// prompt hits the cache and immediately returns nothing — silently disabling
// RAG for the lifetime of the cache entry (5 min). The base query is always
// valid (it came from a non-empty rawPrompt that passed the guard above), so
// falling back to [base] is always safe.
if (limited.length === 0) {
expansionCache.set(cacheKey, [base]);
return [base];
}
// Evict the oldest entry before inserting to stay within the cap.
if (expansionCache.size >= MAX_EXPANSION_CACHE) {
const oldest = expansionCache.keys().next().value;
if (oldest !== undefined)
expansionCache.delete(oldest);
}
expansionCache.set(cacheKey, limited);
return limited;
}