@retrivora-ai/rag-engine 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{MongoDBProvider-QHMGD2LZ.mjs → MongoDBProvider-KGO6N23T.mjs} +1 -1
- package/dist/{PostgreSQLProvider-PJ5ER5Z4.mjs → PostgreSQLProvider-ILWADFAP.mjs} +1 -1
- package/dist/{RagConfig-Ttch1N4d.d.mts → RagConfig--ibz0b3W.d.mts} +4 -0
- package/dist/{RagConfig-Ttch1N4d.d.ts → RagConfig--ibz0b3W.d.ts} +4 -0
- package/dist/{chunk-IUTAZ7QR.mjs → chunk-6GSARSCP.mjs} +9 -2
- package/dist/{chunk-7K4KXB6G.mjs → chunk-CYVPACA7.mjs} +41 -3
- package/dist/{chunk-5HXNKSCR.mjs → chunk-IFPISZ2S.mjs} +8 -1
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +56 -4
- package/dist/handlers/index.mjs +1 -1
- package/dist/{index-rK0KAr2S.d.ts → index-Dr1HN0se.d.ts} +1 -1
- package/dist/{index-sbCtrIRT.d.mts → index-w8qIEFvi.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/server.d.mts +7 -4
- package/dist/server.d.ts +7 -4
- package/dist/server.js +166 -23
- package/dist/server.mjs +113 -22
- package/package.json +1 -1
- package/src/config/RagConfig.ts +4 -0
- package/src/core/Pipeline.ts +60 -1
- package/src/providers/vectordb/MongoDBProvider.ts +12 -1
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +167 -27
- package/src/providers/vectordb/PostgreSQLProvider.ts +11 -2
package/dist/server.js
CHANGED
|
@@ -217,6 +217,12 @@ var PostgreSQLProvider_exports = {};
|
|
|
217
217
|
__export(PostgreSQLProvider_exports, {
|
|
218
218
|
PostgreSQLProvider: () => PostgreSQLProvider
|
|
219
219
|
});
|
|
220
|
+
function stripInternalFilterKeys(filter) {
|
|
221
|
+
if (!filter) return {};
|
|
222
|
+
return Object.fromEntries(
|
|
223
|
+
Object.entries(filter).filter(([key]) => !key.startsWith("__"))
|
|
224
|
+
);
|
|
225
|
+
}
|
|
220
226
|
var import_pg, PostgreSQLProvider;
|
|
221
227
|
var init_PostgreSQLProvider = __esm({
|
|
222
228
|
"src/providers/vectordb/PostgreSQLProvider.ts"() {
|
|
@@ -309,8 +315,9 @@ var init_PostgreSQLProvider = __esm({
|
|
|
309
315
|
let whereClause = namespace ? `WHERE namespace = $3` : "";
|
|
310
316
|
const params = [vectorLiteral, topK];
|
|
311
317
|
if (namespace) params.push(namespace);
|
|
312
|
-
|
|
313
|
-
|
|
318
|
+
const publicFilter = stripInternalFilterKeys(filter);
|
|
319
|
+
if (Object.keys(publicFilter).length > 0) {
|
|
320
|
+
const filterConditions = Object.entries(publicFilter).map(([key, val]) => {
|
|
314
321
|
const paramIdx = params.length + 1;
|
|
315
322
|
params.push(JSON.stringify(val));
|
|
316
323
|
return `metadata->>'${key}' = $${paramIdx}`;
|
|
@@ -360,6 +367,12 @@ var MongoDBProvider_exports = {};
|
|
|
360
367
|
__export(MongoDBProvider_exports, {
|
|
361
368
|
MongoDBProvider: () => MongoDBProvider
|
|
362
369
|
});
|
|
370
|
+
function stripInternalFilterKeys2(filter) {
|
|
371
|
+
if (!filter) return {};
|
|
372
|
+
return Object.fromEntries(
|
|
373
|
+
Object.entries(filter).filter(([key]) => !key.startsWith("__"))
|
|
374
|
+
);
|
|
375
|
+
}
|
|
363
376
|
var import_mongodb, MongoDBProvider;
|
|
364
377
|
var init_MongoDBProvider = __esm({
|
|
365
378
|
"src/providers/vectordb/MongoDBProvider.ts"() {
|
|
@@ -411,6 +424,7 @@ var init_MongoDBProvider = __esm({
|
|
|
411
424
|
await this.collection.bulkWrite(operations);
|
|
412
425
|
}
|
|
413
426
|
async query(vector, topK, namespace, filter) {
|
|
427
|
+
const publicFilter = stripInternalFilterKeys2(filter);
|
|
414
428
|
const pipeline = [
|
|
415
429
|
{
|
|
416
430
|
$vectorSearch: __spreadValues({
|
|
@@ -419,7 +433,7 @@ var init_MongoDBProvider = __esm({
|
|
|
419
433
|
queryVector: vector,
|
|
420
434
|
numCandidates: Math.max(topK * 10, 100),
|
|
421
435
|
limit: topK
|
|
422
|
-
},
|
|
436
|
+
}, Object.keys(publicFilter).length > 0 || namespace ? { filter: __spreadValues(__spreadValues({}, publicFilter), namespace ? { namespace } : {}) } : {})
|
|
423
437
|
},
|
|
424
438
|
{
|
|
425
439
|
$project: {
|
|
@@ -2752,6 +2766,40 @@ var EmbeddingStrategyResolver = class {
|
|
|
2752
2766
|
};
|
|
2753
2767
|
|
|
2754
2768
|
// src/core/Pipeline.ts
|
|
2769
|
+
function normalizeHintValue(value) {
|
|
2770
|
+
return value.replace(/\s+/g, " ").trim();
|
|
2771
|
+
}
|
|
2772
|
+
function extractQueryFieldHints(question) {
|
|
2773
|
+
if (!question.trim()) return [];
|
|
2774
|
+
const hints = /* @__PURE__ */ new Map();
|
|
2775
|
+
const addHint = (value, field) => {
|
|
2776
|
+
const normalizedValue = normalizeHintValue(value);
|
|
2777
|
+
if (!normalizedValue) return;
|
|
2778
|
+
const normalizedField = field ? field.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim() : void 0;
|
|
2779
|
+
const key = `${normalizedField != null ? normalizedField : "*"}::${normalizedValue.toLowerCase()}`;
|
|
2780
|
+
if (!hints.has(key)) {
|
|
2781
|
+
hints.set(key, __spreadValues({
|
|
2782
|
+
value: normalizedValue
|
|
2783
|
+
}, normalizedField ? { field: normalizedField } : {}));
|
|
2784
|
+
}
|
|
2785
|
+
};
|
|
2786
|
+
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
2787
|
+
addHint(match[1]);
|
|
2788
|
+
}
|
|
2789
|
+
const fieldPattern = `([^\\n:=?.!,]{1,60}?)`;
|
|
2790
|
+
const valuePattern = `([^\\n?.!,]{1,120}?)`;
|
|
2791
|
+
const fieldValuePatterns = [
|
|
2792
|
+
new RegExp(`\\b${fieldPattern}\\s*(?:=|:)\\s*["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi"),
|
|
2793
|
+
new RegExp(`\\b${fieldPattern}\\s+(?:is|are|was|were|equals?|equal to|named|called)\\s+["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi"),
|
|
2794
|
+
new RegExp(`\\bwith\\s+${fieldPattern}\\s+["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi")
|
|
2795
|
+
];
|
|
2796
|
+
for (const pattern of fieldValuePatterns) {
|
|
2797
|
+
for (const match of question.matchAll(pattern)) {
|
|
2798
|
+
addHint(match[2], match[1]);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
return [...hints.values()];
|
|
2802
|
+
}
|
|
2755
2803
|
var Pipeline = class {
|
|
2756
2804
|
constructor(config) {
|
|
2757
2805
|
this.initialised = false;
|
|
@@ -2839,7 +2887,11 @@ var Pipeline = class {
|
|
|
2839
2887
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
2840
2888
|
try {
|
|
2841
2889
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: "query" });
|
|
2842
|
-
const
|
|
2890
|
+
const fieldHints = extractQueryFieldHints(question);
|
|
2891
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, {
|
|
2892
|
+
__queryText: question,
|
|
2893
|
+
__fieldHints: fieldHints
|
|
2894
|
+
});
|
|
2843
2895
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
2844
2896
|
const context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
2845
2897
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
@@ -3692,10 +3744,26 @@ init_PostgreSQLProvider();
|
|
|
3692
3744
|
// src/providers/vectordb/MultiTablePostgresProvider.ts
|
|
3693
3745
|
var import_pg2 = require("pg");
|
|
3694
3746
|
init_BaseVectorProvider();
|
|
3747
|
+
function normalizeFieldName(value) {
|
|
3748
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
3749
|
+
}
|
|
3750
|
+
function sanitizeFilterHints(rawFilter) {
|
|
3751
|
+
if (!Array.isArray(rawFilter == null ? void 0 : rawFilter.__fieldHints)) return [];
|
|
3752
|
+
return rawFilter.__fieldHints.filter((hint) => typeof hint === "object" && hint !== null).map((hint) => __spreadValues({
|
|
3753
|
+
value: typeof hint.value === "string" ? hint.value.trim().toLowerCase() : ""
|
|
3754
|
+
}, typeof hint.field === "string" && hint.field.trim() ? { field: normalizeFieldName(hint.field) } : {})).filter((hint) => Boolean(hint.value));
|
|
3755
|
+
}
|
|
3756
|
+
function stripInternalFilterKeys3(filter) {
|
|
3757
|
+
if (!filter) return {};
|
|
3758
|
+
return Object.fromEntries(
|
|
3759
|
+
Object.entries(filter).filter(([key]) => !key.startsWith("__"))
|
|
3760
|
+
);
|
|
3761
|
+
}
|
|
3695
3762
|
var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
3696
3763
|
constructor(config) {
|
|
3697
3764
|
var _a, _b, _c;
|
|
3698
3765
|
super(config);
|
|
3766
|
+
this.tableSearchConfig = /* @__PURE__ */ new Map();
|
|
3699
3767
|
const opts = config.options || {};
|
|
3700
3768
|
if (!opts.connectionString) {
|
|
3701
3769
|
throw new Error("[MultiTablePostgresProvider] options.connectionString is required");
|
|
@@ -3734,6 +3802,7 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3734
3802
|
if (this.tables.length === 0) {
|
|
3735
3803
|
console.warn('[MultiTablePostgresProvider] No tables with "embedding" columns found in the database.');
|
|
3736
3804
|
} else {
|
|
3805
|
+
this.tableSearchConfig = await this.loadTableSearchConfig(client, this.tables);
|
|
3737
3806
|
console.log(
|
|
3738
3807
|
`[MultiTablePostgresProvider] Connected. Searching across ${this.tables.length} table(s): ${this.tables.join(", ")}`
|
|
3739
3808
|
);
|
|
@@ -3763,7 +3832,7 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3763
3832
|
* Query all configured tables and merge results, sorted by cosine similarity score.
|
|
3764
3833
|
*/
|
|
3765
3834
|
async query(vector, topK, _namespace, _filter) {
|
|
3766
|
-
var _a, _b
|
|
3835
|
+
var _a, _b;
|
|
3767
3836
|
if (!this.pool) {
|
|
3768
3837
|
throw new Error("[MultiTablePostgresProvider] Provider not initialized. Call initialize() first.");
|
|
3769
3838
|
}
|
|
@@ -3771,22 +3840,51 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3771
3840
|
const allResults = [];
|
|
3772
3841
|
console.log(`[MultiTablePostgresProvider] --- Starting Multi-Table Search ---`);
|
|
3773
3842
|
const queryText = _filter == null ? void 0 : _filter.__queryText;
|
|
3843
|
+
const fieldHints = sanitizeFilterHints(_filter);
|
|
3844
|
+
const explicitFilter = stripInternalFilterKeys3(_filter);
|
|
3774
3845
|
const queryPromises = this.tables.map(async (table) => {
|
|
3846
|
+
var _a2, _b2, _c;
|
|
3775
3847
|
try {
|
|
3776
3848
|
let sqlQuery = "";
|
|
3777
3849
|
let params = [];
|
|
3850
|
+
const tableConfig = this.tableSearchConfig.get(table);
|
|
3851
|
+
const availableFields = (_a2 = tableConfig == null ? void 0 : tableConfig.availableFields) != null ? _a2 : [];
|
|
3852
|
+
const searchableFields = (_b2 = tableConfig == null ? void 0 : tableConfig.searchableFields) != null ? _b2 : [];
|
|
3853
|
+
const normalizedFieldMap = (_c = tableConfig == null ? void 0 : tableConfig.normalizedFieldMap) != null ? _c : /* @__PURE__ */ new Map();
|
|
3854
|
+
const whereConditions = [];
|
|
3778
3855
|
if (queryText) {
|
|
3856
|
+
const genericValueHints = fieldHints.filter((hint) => !hint.field).map((hint) => hint.value);
|
|
3857
|
+
params = genericValueHints.length > 0 ? [vectorLiteral, queryText, genericValueHints] : [vectorLiteral, queryText];
|
|
3858
|
+
const genericValueParamIndex = genericValueHints.length > 0 ? 3 : -1;
|
|
3859
|
+
const exactValueScoreExpr = genericValueHints.length > 0 && searchableFields.length > 0 ? `GREATEST(${searchableFields.map(
|
|
3860
|
+
(field) => `CASE WHEN LOWER(COALESCE(to_jsonb(t)->>'${field}', '')) = ANY($${genericValueParamIndex}::text[]) THEN 1 ELSE 0 END`
|
|
3861
|
+
).join(", ")})` : "0";
|
|
3862
|
+
const fieldSpecificChecks = fieldHints.filter((hint) => hint.field).map((hint) => {
|
|
3863
|
+
const column = hint.field ? normalizedFieldMap.get(hint.field) : void 0;
|
|
3864
|
+
if (!column) return null;
|
|
3865
|
+
params.push(hint.value);
|
|
3866
|
+
const paramIndex = params.length;
|
|
3867
|
+
return `CASE WHEN LOWER(COALESCE(to_jsonb(t)->>'${column}', '')) = $${paramIndex}::text THEN 1 ELSE 0 END`;
|
|
3868
|
+
}).filter((expr) => Boolean(expr));
|
|
3869
|
+
const exactFieldScoreExpr = fieldSpecificChecks.length > 0 ? `GREATEST(${fieldSpecificChecks.join(", ")})` : "0";
|
|
3779
3870
|
sqlQuery = `
|
|
3780
3871
|
SELECT *,
|
|
3781
3872
|
(1 - (embedding <=> $1::vector)) AS vector_score,
|
|
3782
3873
|
COALESCE(ts_rank(to_tsvector('english', t.*::text), NULLIF(REPLACE(plainto_tsquery('english', $2)::text, '&', '|'), '')::tsquery), 0) AS keyword_score,
|
|
3783
|
-
|
|
3874
|
+
${exactValueScoreExpr} AS exact_value_score,
|
|
3875
|
+
${exactFieldScoreExpr} AS exact_field_score,
|
|
3876
|
+
(
|
|
3877
|
+
(1 - (embedding <=> $1::vector)) +
|
|
3878
|
+
(COALESCE(ts_rank(to_tsvector('english', t.*::text), NULLIF(REPLACE(plainto_tsquery('english', $2)::text, '&', '|'), '')::tsquery), 0) * 2.0) +
|
|
3879
|
+
(${exactValueScoreExpr} * 3.0) +
|
|
3880
|
+
(${exactFieldScoreExpr} * 5.0)
|
|
3881
|
+
) AS hybrid_score
|
|
3784
3882
|
FROM "${table}" t
|
|
3785
3883
|
ORDER BY hybrid_score DESC
|
|
3786
3884
|
LIMIT 50
|
|
3787
3885
|
`;
|
|
3788
|
-
params = [vectorLiteral, queryText];
|
|
3789
3886
|
} else {
|
|
3887
|
+
params = [vectorLiteral];
|
|
3790
3888
|
sqlQuery = `
|
|
3791
3889
|
SELECT *,
|
|
3792
3890
|
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
@@ -3794,7 +3892,16 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3794
3892
|
ORDER BY hybrid_score DESC
|
|
3795
3893
|
LIMIT 50
|
|
3796
3894
|
`;
|
|
3797
|
-
|
|
3895
|
+
}
|
|
3896
|
+
for (const [key, value] of Object.entries(explicitFilter)) {
|
|
3897
|
+
const column = availableFields.includes(key) ? key : normalizedFieldMap.get(normalizeFieldName(key));
|
|
3898
|
+
if (!column) continue;
|
|
3899
|
+
params.push(String(value));
|
|
3900
|
+
whereConditions.push(`COALESCE(to_jsonb(t)->>'${column}', '') = $${params.length}::text`);
|
|
3901
|
+
}
|
|
3902
|
+
if (whereConditions.length > 0) {
|
|
3903
|
+
sqlQuery = sqlQuery.replace(`FROM "${table}" t`, `FROM "${table}" t
|
|
3904
|
+
WHERE ${whereConditions.join(" AND ")}`);
|
|
3798
3905
|
}
|
|
3799
3906
|
const result = await this.pool.query(sqlQuery, params);
|
|
3800
3907
|
if (result.rowCount && result.rowCount > 0) {
|
|
@@ -3804,10 +3911,12 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3804
3911
|
}
|
|
3805
3912
|
const tableResults = [];
|
|
3806
3913
|
for (const row of result.rows) {
|
|
3807
|
-
const
|
|
3914
|
+
const _d = row, { hybrid_score, id } = _d, rest = __objRest(_d, ["hybrid_score", "id"]);
|
|
3808
3915
|
delete rest.embedding;
|
|
3809
3916
|
delete rest.vector_score;
|
|
3810
3917
|
delete rest.keyword_score;
|
|
3918
|
+
delete rest.exact_value_score;
|
|
3919
|
+
delete rest.exact_field_score;
|
|
3811
3920
|
const content = `[TYPE: ${table.replace(/s$/, "").toUpperCase()}]
|
|
3812
3921
|
` + Object.entries(rest).filter(([k, v]) => v !== null && typeof v !== "object" && k !== "id").map(([k, v]) => `${k}: ${v}`).join("\n");
|
|
3813
3922
|
tableResults.push({
|
|
@@ -3830,23 +3939,12 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3830
3939
|
for (const tableResults of resultsArray) {
|
|
3831
3940
|
allResults.push(...tableResults);
|
|
3832
3941
|
}
|
|
3833
|
-
const
|
|
3834
|
-
for (const res of allResults) {
|
|
3835
|
-
const table = (_a = res.metadata) == null ? void 0 : _a.source_table;
|
|
3836
|
-
if (!resultsByTable[table]) resultsByTable[table] = [];
|
|
3837
|
-
resultsByTable[table].push(res);
|
|
3838
|
-
}
|
|
3839
|
-
const balancedResults = [];
|
|
3840
|
-
const tables = Object.keys(resultsByTable);
|
|
3841
|
-
for (const table of tables) {
|
|
3842
|
-
balancedResults.push(...resultsByTable[table].slice(0, 3));
|
|
3843
|
-
}
|
|
3844
|
-
const finalSorted = balancedResults.sort((a, b) => b.score - a.score);
|
|
3942
|
+
const finalSorted = allResults.sort((a, b) => b.score - a.score);
|
|
3845
3943
|
if (finalSorted.length > 0) {
|
|
3846
3944
|
console.log(`[MultiTablePostgresProvider] --- Search Complete ---`);
|
|
3847
|
-
console.log(`[MultiTablePostgresProvider] Final top match from "${(
|
|
3945
|
+
console.log(`[MultiTablePostgresProvider] Final top match from "${(_b = (_a = finalSorted[0].metadata) == null ? void 0 : _a.source_table) != null ? _b : "unknown"}" with score ${finalSorted[0].score.toFixed(4)}`);
|
|
3848
3946
|
}
|
|
3849
|
-
return finalSorted.slice(0,
|
|
3947
|
+
return finalSorted.slice(0, topK);
|
|
3850
3948
|
}
|
|
3851
3949
|
async delete(_id, _namespace) {
|
|
3852
3950
|
console.warn("[MultiTablePostgresProvider] delete() is a no-op for multi-table mode.");
|
|
@@ -3867,6 +3965,51 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3867
3965
|
await this.pool.end();
|
|
3868
3966
|
}
|
|
3869
3967
|
}
|
|
3968
|
+
async loadTableSearchConfig(client, tables) {
|
|
3969
|
+
var _a, _b;
|
|
3970
|
+
if (tables.length === 0) return /* @__PURE__ */ new Map();
|
|
3971
|
+
const configuredSearchFields = this.parseConfiguredSearchFields();
|
|
3972
|
+
const result = await client.query(`
|
|
3973
|
+
SELECT table_name, column_name, data_type
|
|
3974
|
+
FROM information_schema.columns
|
|
3975
|
+
WHERE table_schema = 'public'
|
|
3976
|
+
AND table_name = ANY($1::text[])
|
|
3977
|
+
ORDER BY ordinal_position
|
|
3978
|
+
`, [tables]);
|
|
3979
|
+
const rowsByTable = /* @__PURE__ */ new Map();
|
|
3980
|
+
for (const row of result.rows) {
|
|
3981
|
+
const rows = (_a = rowsByTable.get(row.table_name)) != null ? _a : [];
|
|
3982
|
+
rows.push({ column_name: row.column_name, data_type: row.data_type });
|
|
3983
|
+
rowsByTable.set(row.table_name, rows);
|
|
3984
|
+
}
|
|
3985
|
+
const configByTable = /* @__PURE__ */ new Map();
|
|
3986
|
+
for (const table of tables) {
|
|
3987
|
+
const columns = (_b = rowsByTable.get(table)) != null ? _b : [];
|
|
3988
|
+
const availableFields = columns.filter(({ column_name }) => column_name !== "embedding").map(({ column_name }) => column_name);
|
|
3989
|
+
const inferredFields = columns.filter(
|
|
3990
|
+
({ column_name, data_type }) => column_name !== "embedding" && !["ARRAY", "json", "jsonb", "bytea", "tsvector", "USER-DEFINED"].includes(data_type)
|
|
3991
|
+
).map(({ column_name }) => column_name);
|
|
3992
|
+
const searchableFields = configuredSearchFields.length > 0 ? configuredSearchFields.filter((field) => columns.some((column) => column.column_name === field)) : inferredFields;
|
|
3993
|
+
const normalizedFieldMap = /* @__PURE__ */ new Map();
|
|
3994
|
+
for (const field of availableFields) {
|
|
3995
|
+
normalizedFieldMap.set(normalizeFieldName(field), field);
|
|
3996
|
+
}
|
|
3997
|
+
configByTable.set(table, {
|
|
3998
|
+
availableFields,
|
|
3999
|
+
searchableFields,
|
|
4000
|
+
normalizedFieldMap
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
4003
|
+
return configByTable;
|
|
4004
|
+
}
|
|
4005
|
+
parseConfiguredSearchFields() {
|
|
4006
|
+
var _a;
|
|
4007
|
+
const raw = (_a = this.config.options) == null ? void 0 : _a.searchFields;
|
|
4008
|
+
if (Array.isArray(raw)) {
|
|
4009
|
+
return raw.filter((field) => typeof field === "string" && field.trim().length > 0);
|
|
4010
|
+
}
|
|
4011
|
+
return [];
|
|
4012
|
+
}
|
|
3870
4013
|
};
|
|
3871
4014
|
|
|
3872
4015
|
// src/server.ts
|
package/dist/server.mjs
CHANGED
|
@@ -34,17 +34,17 @@ import {
|
|
|
34
34
|
createIngestHandler,
|
|
35
35
|
createUploadHandler,
|
|
36
36
|
getRagConfig
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-CYVPACA7.mjs";
|
|
38
38
|
import "./chunk-EDLTMSNY.mjs";
|
|
39
39
|
import {
|
|
40
40
|
PineconeProvider
|
|
41
41
|
} from "./chunk-BMHJTWSU.mjs";
|
|
42
42
|
import {
|
|
43
43
|
PostgreSQLProvider
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-6GSARSCP.mjs";
|
|
45
45
|
import {
|
|
46
46
|
MongoDBProvider
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-IFPISZ2S.mjs";
|
|
48
48
|
import {
|
|
49
49
|
MilvusProvider
|
|
50
50
|
} from "./chunk-VEJNRS4B.mjs";
|
|
@@ -335,10 +335,26 @@ function createFromPreset(presetName) {
|
|
|
335
335
|
|
|
336
336
|
// src/providers/vectordb/MultiTablePostgresProvider.ts
|
|
337
337
|
import { Pool } from "pg";
|
|
338
|
+
function normalizeFieldName(value) {
|
|
339
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
340
|
+
}
|
|
341
|
+
function sanitizeFilterHints(rawFilter) {
|
|
342
|
+
if (!Array.isArray(rawFilter == null ? void 0 : rawFilter.__fieldHints)) return [];
|
|
343
|
+
return rawFilter.__fieldHints.filter((hint) => typeof hint === "object" && hint !== null).map((hint) => __spreadValues({
|
|
344
|
+
value: typeof hint.value === "string" ? hint.value.trim().toLowerCase() : ""
|
|
345
|
+
}, typeof hint.field === "string" && hint.field.trim() ? { field: normalizeFieldName(hint.field) } : {})).filter((hint) => Boolean(hint.value));
|
|
346
|
+
}
|
|
347
|
+
function stripInternalFilterKeys(filter) {
|
|
348
|
+
if (!filter) return {};
|
|
349
|
+
return Object.fromEntries(
|
|
350
|
+
Object.entries(filter).filter(([key]) => !key.startsWith("__"))
|
|
351
|
+
);
|
|
352
|
+
}
|
|
338
353
|
var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
339
354
|
constructor(config) {
|
|
340
355
|
var _a, _b, _c;
|
|
341
356
|
super(config);
|
|
357
|
+
this.tableSearchConfig = /* @__PURE__ */ new Map();
|
|
342
358
|
const opts = config.options || {};
|
|
343
359
|
if (!opts.connectionString) {
|
|
344
360
|
throw new Error("[MultiTablePostgresProvider] options.connectionString is required");
|
|
@@ -377,6 +393,7 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
377
393
|
if (this.tables.length === 0) {
|
|
378
394
|
console.warn('[MultiTablePostgresProvider] No tables with "embedding" columns found in the database.');
|
|
379
395
|
} else {
|
|
396
|
+
this.tableSearchConfig = await this.loadTableSearchConfig(client, this.tables);
|
|
380
397
|
console.log(
|
|
381
398
|
`[MultiTablePostgresProvider] Connected. Searching across ${this.tables.length} table(s): ${this.tables.join(", ")}`
|
|
382
399
|
);
|
|
@@ -406,7 +423,7 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
406
423
|
* Query all configured tables and merge results, sorted by cosine similarity score.
|
|
407
424
|
*/
|
|
408
425
|
async query(vector, topK, _namespace, _filter) {
|
|
409
|
-
var _a, _b
|
|
426
|
+
var _a, _b;
|
|
410
427
|
if (!this.pool) {
|
|
411
428
|
throw new Error("[MultiTablePostgresProvider] Provider not initialized. Call initialize() first.");
|
|
412
429
|
}
|
|
@@ -414,22 +431,51 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
414
431
|
const allResults = [];
|
|
415
432
|
console.log(`[MultiTablePostgresProvider] --- Starting Multi-Table Search ---`);
|
|
416
433
|
const queryText = _filter == null ? void 0 : _filter.__queryText;
|
|
434
|
+
const fieldHints = sanitizeFilterHints(_filter);
|
|
435
|
+
const explicitFilter = stripInternalFilterKeys(_filter);
|
|
417
436
|
const queryPromises = this.tables.map(async (table) => {
|
|
437
|
+
var _a2, _b2, _c;
|
|
418
438
|
try {
|
|
419
439
|
let sqlQuery = "";
|
|
420
440
|
let params = [];
|
|
441
|
+
const tableConfig = this.tableSearchConfig.get(table);
|
|
442
|
+
const availableFields = (_a2 = tableConfig == null ? void 0 : tableConfig.availableFields) != null ? _a2 : [];
|
|
443
|
+
const searchableFields = (_b2 = tableConfig == null ? void 0 : tableConfig.searchableFields) != null ? _b2 : [];
|
|
444
|
+
const normalizedFieldMap = (_c = tableConfig == null ? void 0 : tableConfig.normalizedFieldMap) != null ? _c : /* @__PURE__ */ new Map();
|
|
445
|
+
const whereConditions = [];
|
|
421
446
|
if (queryText) {
|
|
447
|
+
const genericValueHints = fieldHints.filter((hint) => !hint.field).map((hint) => hint.value);
|
|
448
|
+
params = genericValueHints.length > 0 ? [vectorLiteral, queryText, genericValueHints] : [vectorLiteral, queryText];
|
|
449
|
+
const genericValueParamIndex = genericValueHints.length > 0 ? 3 : -1;
|
|
450
|
+
const exactValueScoreExpr = genericValueHints.length > 0 && searchableFields.length > 0 ? `GREATEST(${searchableFields.map(
|
|
451
|
+
(field) => `CASE WHEN LOWER(COALESCE(to_jsonb(t)->>'${field}', '')) = ANY($${genericValueParamIndex}::text[]) THEN 1 ELSE 0 END`
|
|
452
|
+
).join(", ")})` : "0";
|
|
453
|
+
const fieldSpecificChecks = fieldHints.filter((hint) => hint.field).map((hint) => {
|
|
454
|
+
const column = hint.field ? normalizedFieldMap.get(hint.field) : void 0;
|
|
455
|
+
if (!column) return null;
|
|
456
|
+
params.push(hint.value);
|
|
457
|
+
const paramIndex = params.length;
|
|
458
|
+
return `CASE WHEN LOWER(COALESCE(to_jsonb(t)->>'${column}', '')) = $${paramIndex}::text THEN 1 ELSE 0 END`;
|
|
459
|
+
}).filter((expr) => Boolean(expr));
|
|
460
|
+
const exactFieldScoreExpr = fieldSpecificChecks.length > 0 ? `GREATEST(${fieldSpecificChecks.join(", ")})` : "0";
|
|
422
461
|
sqlQuery = `
|
|
423
462
|
SELECT *,
|
|
424
463
|
(1 - (embedding <=> $1::vector)) AS vector_score,
|
|
425
464
|
COALESCE(ts_rank(to_tsvector('english', t.*::text), NULLIF(REPLACE(plainto_tsquery('english', $2)::text, '&', '|'), '')::tsquery), 0) AS keyword_score,
|
|
426
|
-
|
|
465
|
+
${exactValueScoreExpr} AS exact_value_score,
|
|
466
|
+
${exactFieldScoreExpr} AS exact_field_score,
|
|
467
|
+
(
|
|
468
|
+
(1 - (embedding <=> $1::vector)) +
|
|
469
|
+
(COALESCE(ts_rank(to_tsvector('english', t.*::text), NULLIF(REPLACE(plainto_tsquery('english', $2)::text, '&', '|'), '')::tsquery), 0) * 2.0) +
|
|
470
|
+
(${exactValueScoreExpr} * 3.0) +
|
|
471
|
+
(${exactFieldScoreExpr} * 5.0)
|
|
472
|
+
) AS hybrid_score
|
|
427
473
|
FROM "${table}" t
|
|
428
474
|
ORDER BY hybrid_score DESC
|
|
429
475
|
LIMIT 50
|
|
430
476
|
`;
|
|
431
|
-
params = [vectorLiteral, queryText];
|
|
432
477
|
} else {
|
|
478
|
+
params = [vectorLiteral];
|
|
433
479
|
sqlQuery = `
|
|
434
480
|
SELECT *,
|
|
435
481
|
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
@@ -437,7 +483,16 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
437
483
|
ORDER BY hybrid_score DESC
|
|
438
484
|
LIMIT 50
|
|
439
485
|
`;
|
|
440
|
-
|
|
486
|
+
}
|
|
487
|
+
for (const [key, value] of Object.entries(explicitFilter)) {
|
|
488
|
+
const column = availableFields.includes(key) ? key : normalizedFieldMap.get(normalizeFieldName(key));
|
|
489
|
+
if (!column) continue;
|
|
490
|
+
params.push(String(value));
|
|
491
|
+
whereConditions.push(`COALESCE(to_jsonb(t)->>'${column}', '') = $${params.length}::text`);
|
|
492
|
+
}
|
|
493
|
+
if (whereConditions.length > 0) {
|
|
494
|
+
sqlQuery = sqlQuery.replace(`FROM "${table}" t`, `FROM "${table}" t
|
|
495
|
+
WHERE ${whereConditions.join(" AND ")}`);
|
|
441
496
|
}
|
|
442
497
|
const result = await this.pool.query(sqlQuery, params);
|
|
443
498
|
if (result.rowCount && result.rowCount > 0) {
|
|
@@ -447,10 +502,12 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
447
502
|
}
|
|
448
503
|
const tableResults = [];
|
|
449
504
|
for (const row of result.rows) {
|
|
450
|
-
const
|
|
505
|
+
const _d = row, { hybrid_score, id } = _d, rest = __objRest(_d, ["hybrid_score", "id"]);
|
|
451
506
|
delete rest.embedding;
|
|
452
507
|
delete rest.vector_score;
|
|
453
508
|
delete rest.keyword_score;
|
|
509
|
+
delete rest.exact_value_score;
|
|
510
|
+
delete rest.exact_field_score;
|
|
454
511
|
const content = `[TYPE: ${table.replace(/s$/, "").toUpperCase()}]
|
|
455
512
|
` + Object.entries(rest).filter(([k, v]) => v !== null && typeof v !== "object" && k !== "id").map(([k, v]) => `${k}: ${v}`).join("\n");
|
|
456
513
|
tableResults.push({
|
|
@@ -473,23 +530,12 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
473
530
|
for (const tableResults of resultsArray) {
|
|
474
531
|
allResults.push(...tableResults);
|
|
475
532
|
}
|
|
476
|
-
const
|
|
477
|
-
for (const res of allResults) {
|
|
478
|
-
const table = (_a = res.metadata) == null ? void 0 : _a.source_table;
|
|
479
|
-
if (!resultsByTable[table]) resultsByTable[table] = [];
|
|
480
|
-
resultsByTable[table].push(res);
|
|
481
|
-
}
|
|
482
|
-
const balancedResults = [];
|
|
483
|
-
const tables = Object.keys(resultsByTable);
|
|
484
|
-
for (const table of tables) {
|
|
485
|
-
balancedResults.push(...resultsByTable[table].slice(0, 3));
|
|
486
|
-
}
|
|
487
|
-
const finalSorted = balancedResults.sort((a, b) => b.score - a.score);
|
|
533
|
+
const finalSorted = allResults.sort((a, b) => b.score - a.score);
|
|
488
534
|
if (finalSorted.length > 0) {
|
|
489
535
|
console.log(`[MultiTablePostgresProvider] --- Search Complete ---`);
|
|
490
|
-
console.log(`[MultiTablePostgresProvider] Final top match from "${(
|
|
536
|
+
console.log(`[MultiTablePostgresProvider] Final top match from "${(_b = (_a = finalSorted[0].metadata) == null ? void 0 : _a.source_table) != null ? _b : "unknown"}" with score ${finalSorted[0].score.toFixed(4)}`);
|
|
491
537
|
}
|
|
492
|
-
return finalSorted.slice(0,
|
|
538
|
+
return finalSorted.slice(0, topK);
|
|
493
539
|
}
|
|
494
540
|
async delete(_id, _namespace) {
|
|
495
541
|
console.warn("[MultiTablePostgresProvider] delete() is a no-op for multi-table mode.");
|
|
@@ -510,6 +556,51 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
510
556
|
await this.pool.end();
|
|
511
557
|
}
|
|
512
558
|
}
|
|
559
|
+
async loadTableSearchConfig(client, tables) {
|
|
560
|
+
var _a, _b;
|
|
561
|
+
if (tables.length === 0) return /* @__PURE__ */ new Map();
|
|
562
|
+
const configuredSearchFields = this.parseConfiguredSearchFields();
|
|
563
|
+
const result = await client.query(`
|
|
564
|
+
SELECT table_name, column_name, data_type
|
|
565
|
+
FROM information_schema.columns
|
|
566
|
+
WHERE table_schema = 'public'
|
|
567
|
+
AND table_name = ANY($1::text[])
|
|
568
|
+
ORDER BY ordinal_position
|
|
569
|
+
`, [tables]);
|
|
570
|
+
const rowsByTable = /* @__PURE__ */ new Map();
|
|
571
|
+
for (const row of result.rows) {
|
|
572
|
+
const rows = (_a = rowsByTable.get(row.table_name)) != null ? _a : [];
|
|
573
|
+
rows.push({ column_name: row.column_name, data_type: row.data_type });
|
|
574
|
+
rowsByTable.set(row.table_name, rows);
|
|
575
|
+
}
|
|
576
|
+
const configByTable = /* @__PURE__ */ new Map();
|
|
577
|
+
for (const table of tables) {
|
|
578
|
+
const columns = (_b = rowsByTable.get(table)) != null ? _b : [];
|
|
579
|
+
const availableFields = columns.filter(({ column_name }) => column_name !== "embedding").map(({ column_name }) => column_name);
|
|
580
|
+
const inferredFields = columns.filter(
|
|
581
|
+
({ column_name, data_type }) => column_name !== "embedding" && !["ARRAY", "json", "jsonb", "bytea", "tsvector", "USER-DEFINED"].includes(data_type)
|
|
582
|
+
).map(({ column_name }) => column_name);
|
|
583
|
+
const searchableFields = configuredSearchFields.length > 0 ? configuredSearchFields.filter((field) => columns.some((column) => column.column_name === field)) : inferredFields;
|
|
584
|
+
const normalizedFieldMap = /* @__PURE__ */ new Map();
|
|
585
|
+
for (const field of availableFields) {
|
|
586
|
+
normalizedFieldMap.set(normalizeFieldName(field), field);
|
|
587
|
+
}
|
|
588
|
+
configByTable.set(table, {
|
|
589
|
+
availableFields,
|
|
590
|
+
searchableFields,
|
|
591
|
+
normalizedFieldMap
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
return configByTable;
|
|
595
|
+
}
|
|
596
|
+
parseConfiguredSearchFields() {
|
|
597
|
+
var _a;
|
|
598
|
+
const raw = (_a = this.config.options) == null ? void 0 : _a.searchFields;
|
|
599
|
+
if (Array.isArray(raw)) {
|
|
600
|
+
return raw.filter((field) => typeof field === "string" && field.trim().length > 0);
|
|
601
|
+
}
|
|
602
|
+
return [];
|
|
603
|
+
}
|
|
513
604
|
};
|
|
514
605
|
export {
|
|
515
606
|
AnthropicProvider,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
|
|
5
5
|
"author": "Abhinav Alkuchi",
|
|
6
6
|
"license": "MIT",
|
package/src/config/RagConfig.ts
CHANGED
|
@@ -28,6 +28,10 @@ export interface VectorDBConfig {
|
|
|
28
28
|
* - idPath?: string (e.g. '_id')
|
|
29
29
|
* - scorePath?: string (e.g. 'similarity')
|
|
30
30
|
* - contentPath?: string (e.g. 'text')
|
|
31
|
+
*
|
|
32
|
+
* For multi-table PostgreSQL search, the following options are also supported:
|
|
33
|
+
* - tables?: string[] | string
|
|
34
|
+
* - searchFields?: string[] // optional override for which columns receive exact-match boosts
|
|
31
35
|
*/
|
|
32
36
|
options: Record<string, unknown>;
|
|
33
37
|
}
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -7,6 +7,61 @@ import { BatchProcessor, BatchOptions } from './BatchProcessor';
|
|
|
7
7
|
import { EmbeddingStrategyResolver } from '../config/EmbeddingStrategy';
|
|
8
8
|
import { IngestDocument, ChatResponse } from '../types';
|
|
9
9
|
|
|
10
|
+
interface QueryFieldHint {
|
|
11
|
+
field?: string;
|
|
12
|
+
value: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeHintValue(value: string): string {
|
|
16
|
+
return value.replace(/\s+/g, ' ').trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function extractQueryFieldHints(question: string): QueryFieldHint[] {
|
|
20
|
+
if (!question.trim()) return [];
|
|
21
|
+
|
|
22
|
+
const hints = new Map<string, QueryFieldHint>();
|
|
23
|
+
|
|
24
|
+
const addHint = (value: string, field?: string) => {
|
|
25
|
+
const normalizedValue = normalizeHintValue(value);
|
|
26
|
+
if (!normalizedValue) return;
|
|
27
|
+
|
|
28
|
+
const normalizedField = field
|
|
29
|
+
? field
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
32
|
+
.trim()
|
|
33
|
+
: undefined;
|
|
34
|
+
|
|
35
|
+
const key = `${normalizedField ?? '*'}::${normalizedValue.toLowerCase()}`;
|
|
36
|
+
if (!hints.has(key)) {
|
|
37
|
+
hints.set(key, {
|
|
38
|
+
value: normalizedValue,
|
|
39
|
+
...(normalizedField ? { field: normalizedField } : {}),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
45
|
+
addHint(match[1]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const fieldPattern = `([^\\n:=?.!,]{1,60}?)`;
|
|
49
|
+
const valuePattern = `([^\\n?.!,]{1,120}?)`;
|
|
50
|
+
const fieldValuePatterns = [
|
|
51
|
+
new RegExp(`\\b${fieldPattern}\\s*(?:=|:)\\s*["']?${valuePattern}["']?(?=[?.!,]|$)`, 'gi'),
|
|
52
|
+
new RegExp(`\\b${fieldPattern}\\s+(?:is|are|was|were|equals?|equal to|named|called)\\s+["']?${valuePattern}["']?(?=[?.!,]|$)`, 'gi'),
|
|
53
|
+
new RegExp(`\\bwith\\s+${fieldPattern}\\s+["']?${valuePattern}["']?(?=[?.!,]|$)`, 'gi'),
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
for (const pattern of fieldValuePatterns) {
|
|
57
|
+
for (const match of question.matchAll(pattern)) {
|
|
58
|
+
addHint(match[2], match[1]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return [...hints.values()];
|
|
63
|
+
}
|
|
64
|
+
|
|
10
65
|
/**
|
|
11
66
|
* Pipeline — orchestrates the RAG flow: Embed → Search → Augment → Generate.
|
|
12
67
|
*
|
|
@@ -135,8 +190,12 @@ export class Pipeline {
|
|
|
135
190
|
|
|
136
191
|
try {
|
|
137
192
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: 'query' });
|
|
193
|
+
const fieldHints = extractQueryFieldHints(question);
|
|
138
194
|
// Pass the original text to the vector DB so it can perform hybrid (keyword) search if supported
|
|
139
|
-
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, {
|
|
195
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, {
|
|
196
|
+
__queryText: question,
|
|
197
|
+
__fieldHints: fieldHints,
|
|
198
|
+
});
|
|
140
199
|
|
|
141
200
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
142
201
|
const context = sources.length
|
|
@@ -3,6 +3,14 @@ import { VectorDBConfig } from '../../config/RagConfig';
|
|
|
3
3
|
import { BaseVectorProvider } from './BaseVectorProvider';
|
|
4
4
|
import { VectorMatch, UpsertDocument } from '../../types';
|
|
5
5
|
|
|
6
|
+
function stripInternalFilterKeys(filter?: Record<string, unknown>): Record<string, unknown> {
|
|
7
|
+
if (!filter) return {};
|
|
8
|
+
|
|
9
|
+
return Object.fromEntries(
|
|
10
|
+
Object.entries(filter).filter(([key]) => !key.startsWith('__'))
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
/**
|
|
7
15
|
* MongoDBProvider — MongoDB Atlas Vector Search implementation.
|
|
8
16
|
*/
|
|
@@ -67,6 +75,7 @@ export class MongoDBProvider extends BaseVectorProvider {
|
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
async query(vector: number[], topK: number, namespace?: string, filter?: Record<string, unknown>): Promise<VectorMatch[]> {
|
|
78
|
+
const publicFilter = stripInternalFilterKeys(filter);
|
|
70
79
|
const pipeline: Record<string, unknown>[] = [
|
|
71
80
|
{
|
|
72
81
|
$vectorSearch: {
|
|
@@ -75,7 +84,9 @@ export class MongoDBProvider extends BaseVectorProvider {
|
|
|
75
84
|
queryVector: vector,
|
|
76
85
|
numCandidates: Math.max(topK * 10, 100),
|
|
77
86
|
limit: topK,
|
|
78
|
-
...(
|
|
87
|
+
...(Object.keys(publicFilter).length > 0 || namespace
|
|
88
|
+
? { filter: { ...publicFilter, ...(namespace ? { namespace } : {}) } }
|
|
89
|
+
: {}),
|
|
79
90
|
},
|
|
80
91
|
},
|
|
81
92
|
{
|