@retrivora-ai/rag-engine 0.2.3 → 0.2.5
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/{chunk-K53N7SEE.mjs → chunk-7K4KXB6G.mjs} +1 -1
- package/dist/handlers/index.js +1 -1
- package/dist/handlers/index.mjs +1 -1
- package/dist/server.d.mts +1 -2
- package/dist/server.d.ts +1 -2
- package/dist/server.js +43 -17
- package/dist/server.mjs +43 -17
- package/package.json +1 -1
- package/src/core/Pipeline.ts +2 -1
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +56 -24
|
@@ -1640,7 +1640,7 @@ var Pipeline = class {
|
|
|
1640
1640
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
1641
1641
|
try {
|
|
1642
1642
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: "query" });
|
|
1643
|
-
const rawMatches = await this.vectorDB.query(queryVector, topK, ns);
|
|
1643
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, { __queryText: question });
|
|
1644
1644
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
1645
1645
|
const context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
1646
1646
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
package/dist/handlers/index.js
CHANGED
|
@@ -2750,7 +2750,7 @@ var Pipeline = class {
|
|
|
2750
2750
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
2751
2751
|
try {
|
|
2752
2752
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: "query" });
|
|
2753
|
-
const rawMatches = await this.vectorDB.query(queryVector, topK, ns);
|
|
2753
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, { __queryText: question });
|
|
2754
2754
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
2755
2755
|
const context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
2756
2756
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
package/dist/handlers/index.mjs
CHANGED
package/dist/server.d.mts
CHANGED
|
@@ -720,8 +720,7 @@ declare class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
720
720
|
/**
|
|
721
721
|
* Query all configured tables and merge results, sorted by cosine similarity score.
|
|
722
722
|
*/
|
|
723
|
-
query(vector: number[], topK: number, _namespace?: string,
|
|
724
|
-
_filter?: Record<string, unknown>): Promise<VectorMatch[]>;
|
|
723
|
+
query(vector: number[], topK: number, _namespace?: string, _filter?: Record<string, unknown>): Promise<VectorMatch[]>;
|
|
725
724
|
delete(_id: string | number, _namespace?: string): Promise<void>;
|
|
726
725
|
deleteNamespace(_namespace: string): Promise<void>;
|
|
727
726
|
ping(): Promise<boolean>;
|
package/dist/server.d.ts
CHANGED
|
@@ -720,8 +720,7 @@ declare class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
720
720
|
/**
|
|
721
721
|
* Query all configured tables and merge results, sorted by cosine similarity score.
|
|
722
722
|
*/
|
|
723
|
-
query(vector: number[], topK: number, _namespace?: string,
|
|
724
|
-
_filter?: Record<string, unknown>): Promise<VectorMatch[]>;
|
|
723
|
+
query(vector: number[], topK: number, _namespace?: string, _filter?: Record<string, unknown>): Promise<VectorMatch[]>;
|
|
725
724
|
delete(_id: string | number, _namespace?: string): Promise<void>;
|
|
726
725
|
deleteNamespace(_namespace: string): Promise<void>;
|
|
727
726
|
ping(): Promise<boolean>;
|
package/dist/server.js
CHANGED
|
@@ -2839,7 +2839,7 @@ var Pipeline = class {
|
|
|
2839
2839
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
2840
2840
|
try {
|
|
2841
2841
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: "query" });
|
|
2842
|
-
const rawMatches = await this.vectorDB.query(queryVector, topK, ns);
|
|
2842
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, { __queryText: question });
|
|
2843
2843
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
2844
2844
|
const context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
2845
2845
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
@@ -3763,50 +3763,76 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3763
3763
|
* Query all configured tables and merge results, sorted by cosine similarity score.
|
|
3764
3764
|
*/
|
|
3765
3765
|
async query(vector, topK, _namespace, _filter) {
|
|
3766
|
-
var _b, _c
|
|
3766
|
+
var _a, _b, _c;
|
|
3767
3767
|
if (!this.pool) {
|
|
3768
3768
|
throw new Error("[MultiTablePostgresProvider] Provider not initialized. Call initialize() first.");
|
|
3769
3769
|
}
|
|
3770
3770
|
const vectorLiteral = `[${vector.join(",")}]`;
|
|
3771
3771
|
const allResults = [];
|
|
3772
3772
|
console.log(`[MultiTablePostgresProvider] --- Starting Multi-Table Search ---`);
|
|
3773
|
-
|
|
3773
|
+
const queryText = _filter == null ? void 0 : _filter.__queryText;
|
|
3774
|
+
const queryPromises = this.tables.map(async (table) => {
|
|
3774
3775
|
try {
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3776
|
+
let sqlQuery = "";
|
|
3777
|
+
let params = [];
|
|
3778
|
+
if (queryText) {
|
|
3779
|
+
sqlQuery = `
|
|
3780
|
+
SELECT *,
|
|
3781
|
+
(1 - (embedding <=> $1::vector)) AS vector_score,
|
|
3782
|
+
ts_rank(to_tsvector('english', t.*::text), plainto_tsquery('english', $2)) AS keyword_score,
|
|
3783
|
+
((1 - (embedding <=> $1::vector)) + (ts_rank(to_tsvector('english', t.*::text), plainto_tsquery('english', $2)) * 2.0)) AS hybrid_score
|
|
3784
|
+
FROM "${table}" t
|
|
3785
|
+
ORDER BY hybrid_score DESC
|
|
3786
|
+
LIMIT 50
|
|
3787
|
+
`;
|
|
3788
|
+
params = [vectorLiteral, queryText];
|
|
3789
|
+
} else {
|
|
3790
|
+
sqlQuery = `
|
|
3791
|
+
SELECT *,
|
|
3792
|
+
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
3793
|
+
FROM "${table}" t
|
|
3794
|
+
ORDER BY hybrid_score DESC
|
|
3795
|
+
LIMIT 50
|
|
3796
|
+
`;
|
|
3797
|
+
params = [vectorLiteral];
|
|
3798
|
+
}
|
|
3799
|
+
const result = await this.pool.query(sqlQuery, params);
|
|
3783
3800
|
if (result.rowCount && result.rowCount > 0) {
|
|
3784
|
-
console.log(` \u2705 Table "${table}": Found ${result.rowCount} potential matches. Top score: ${result.rows[0].
|
|
3801
|
+
console.log(` \u2705 Table "${table}": Found ${result.rowCount} potential matches. Top score: ${result.rows[0].hybrid_score.toFixed(4)}`);
|
|
3785
3802
|
} else {
|
|
3786
3803
|
console.log(` \u26AA Table "${table}": No relevant matches found.`);
|
|
3787
3804
|
}
|
|
3805
|
+
const tableResults = [];
|
|
3788
3806
|
for (const row of result.rows) {
|
|
3789
|
-
const
|
|
3807
|
+
const _a2 = row, { hybrid_score, id } = _a2, rest = __objRest(_a2, ["hybrid_score", "id"]);
|
|
3790
3808
|
delete rest.embedding;
|
|
3809
|
+
delete rest.vector_score;
|
|
3810
|
+
delete rest.keyword_score;
|
|
3791
3811
|
const content = `[TYPE: ${table.replace(/s$/, "").toUpperCase()}]
|
|
3792
3812
|
` + Object.entries(rest).filter(([k, v]) => v !== null && typeof v !== "object" && k !== "id").map(([k, v]) => `${k}: ${v}`).join("\n");
|
|
3793
|
-
|
|
3813
|
+
tableResults.push({
|
|
3794
3814
|
id: `${table}-${id}`,
|
|
3795
|
-
score: parseFloat(String(
|
|
3815
|
+
score: parseFloat(String(hybrid_score)),
|
|
3796
3816
|
content,
|
|
3797
3817
|
metadata: __spreadProps(__spreadValues({}, rest), { source_table: table })
|
|
3798
3818
|
});
|
|
3799
3819
|
}
|
|
3820
|
+
return tableResults;
|
|
3800
3821
|
} catch (err) {
|
|
3801
3822
|
console.error(
|
|
3802
3823
|
`[MultiTablePostgresProvider] Error querying table "${table}":`,
|
|
3803
3824
|
err.message
|
|
3804
3825
|
);
|
|
3826
|
+
return [];
|
|
3805
3827
|
}
|
|
3828
|
+
});
|
|
3829
|
+
const resultsArray = await Promise.all(queryPromises);
|
|
3830
|
+
for (const tableResults of resultsArray) {
|
|
3831
|
+
allResults.push(...tableResults);
|
|
3806
3832
|
}
|
|
3807
3833
|
const resultsByTable = {};
|
|
3808
3834
|
for (const res of allResults) {
|
|
3809
|
-
const table = (
|
|
3835
|
+
const table = (_a = res.metadata) == null ? void 0 : _a.source_table;
|
|
3810
3836
|
if (!resultsByTable[table]) resultsByTable[table] = [];
|
|
3811
3837
|
resultsByTable[table].push(res);
|
|
3812
3838
|
}
|
|
@@ -3818,7 +3844,7 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
3818
3844
|
const finalSorted = balancedResults.sort((a, b) => b.score - a.score);
|
|
3819
3845
|
if (finalSorted.length > 0) {
|
|
3820
3846
|
console.log(`[MultiTablePostgresProvider] --- Search Complete ---`);
|
|
3821
|
-
console.log(`[MultiTablePostgresProvider] Final top match from "${(
|
|
3847
|
+
console.log(`[MultiTablePostgresProvider] Final top match from "${(_c = (_b = finalSorted[0].metadata) == null ? void 0 : _b.source_table) != null ? _c : "unknown"}" with score ${finalSorted[0].score.toFixed(4)}`);
|
|
3822
3848
|
}
|
|
3823
3849
|
return finalSorted.slice(0, Math.max(topK, 15));
|
|
3824
3850
|
}
|
package/dist/server.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
createIngestHandler,
|
|
35
35
|
createUploadHandler,
|
|
36
36
|
getRagConfig
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-7K4KXB6G.mjs";
|
|
38
38
|
import "./chunk-EDLTMSNY.mjs";
|
|
39
39
|
import {
|
|
40
40
|
PineconeProvider
|
|
@@ -406,50 +406,76 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
406
406
|
* Query all configured tables and merge results, sorted by cosine similarity score.
|
|
407
407
|
*/
|
|
408
408
|
async query(vector, topK, _namespace, _filter) {
|
|
409
|
-
var _b, _c
|
|
409
|
+
var _a, _b, _c;
|
|
410
410
|
if (!this.pool) {
|
|
411
411
|
throw new Error("[MultiTablePostgresProvider] Provider not initialized. Call initialize() first.");
|
|
412
412
|
}
|
|
413
413
|
const vectorLiteral = `[${vector.join(",")}]`;
|
|
414
414
|
const allResults = [];
|
|
415
415
|
console.log(`[MultiTablePostgresProvider] --- Starting Multi-Table Search ---`);
|
|
416
|
-
|
|
416
|
+
const queryText = _filter == null ? void 0 : _filter.__queryText;
|
|
417
|
+
const queryPromises = this.tables.map(async (table) => {
|
|
417
418
|
try {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
419
|
+
let sqlQuery = "";
|
|
420
|
+
let params = [];
|
|
421
|
+
if (queryText) {
|
|
422
|
+
sqlQuery = `
|
|
423
|
+
SELECT *,
|
|
424
|
+
(1 - (embedding <=> $1::vector)) AS vector_score,
|
|
425
|
+
ts_rank(to_tsvector('english', t.*::text), plainto_tsquery('english', $2)) AS keyword_score,
|
|
426
|
+
((1 - (embedding <=> $1::vector)) + (ts_rank(to_tsvector('english', t.*::text), plainto_tsquery('english', $2)) * 2.0)) AS hybrid_score
|
|
427
|
+
FROM "${table}" t
|
|
428
|
+
ORDER BY hybrid_score DESC
|
|
429
|
+
LIMIT 50
|
|
430
|
+
`;
|
|
431
|
+
params = [vectorLiteral, queryText];
|
|
432
|
+
} else {
|
|
433
|
+
sqlQuery = `
|
|
434
|
+
SELECT *,
|
|
435
|
+
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
436
|
+
FROM "${table}" t
|
|
437
|
+
ORDER BY hybrid_score DESC
|
|
438
|
+
LIMIT 50
|
|
439
|
+
`;
|
|
440
|
+
params = [vectorLiteral];
|
|
441
|
+
}
|
|
442
|
+
const result = await this.pool.query(sqlQuery, params);
|
|
426
443
|
if (result.rowCount && result.rowCount > 0) {
|
|
427
|
-
console.log(` \u2705 Table "${table}": Found ${result.rowCount} potential matches. Top score: ${result.rows[0].
|
|
444
|
+
console.log(` \u2705 Table "${table}": Found ${result.rowCount} potential matches. Top score: ${result.rows[0].hybrid_score.toFixed(4)}`);
|
|
428
445
|
} else {
|
|
429
446
|
console.log(` \u26AA Table "${table}": No relevant matches found.`);
|
|
430
447
|
}
|
|
448
|
+
const tableResults = [];
|
|
431
449
|
for (const row of result.rows) {
|
|
432
|
-
const
|
|
450
|
+
const _a2 = row, { hybrid_score, id } = _a2, rest = __objRest(_a2, ["hybrid_score", "id"]);
|
|
433
451
|
delete rest.embedding;
|
|
452
|
+
delete rest.vector_score;
|
|
453
|
+
delete rest.keyword_score;
|
|
434
454
|
const content = `[TYPE: ${table.replace(/s$/, "").toUpperCase()}]
|
|
435
455
|
` + Object.entries(rest).filter(([k, v]) => v !== null && typeof v !== "object" && k !== "id").map(([k, v]) => `${k}: ${v}`).join("\n");
|
|
436
|
-
|
|
456
|
+
tableResults.push({
|
|
437
457
|
id: `${table}-${id}`,
|
|
438
|
-
score: parseFloat(String(
|
|
458
|
+
score: parseFloat(String(hybrid_score)),
|
|
439
459
|
content,
|
|
440
460
|
metadata: __spreadProps(__spreadValues({}, rest), { source_table: table })
|
|
441
461
|
});
|
|
442
462
|
}
|
|
463
|
+
return tableResults;
|
|
443
464
|
} catch (err) {
|
|
444
465
|
console.error(
|
|
445
466
|
`[MultiTablePostgresProvider] Error querying table "${table}":`,
|
|
446
467
|
err.message
|
|
447
468
|
);
|
|
469
|
+
return [];
|
|
448
470
|
}
|
|
471
|
+
});
|
|
472
|
+
const resultsArray = await Promise.all(queryPromises);
|
|
473
|
+
for (const tableResults of resultsArray) {
|
|
474
|
+
allResults.push(...tableResults);
|
|
449
475
|
}
|
|
450
476
|
const resultsByTable = {};
|
|
451
477
|
for (const res of allResults) {
|
|
452
|
-
const table = (
|
|
478
|
+
const table = (_a = res.metadata) == null ? void 0 : _a.source_table;
|
|
453
479
|
if (!resultsByTable[table]) resultsByTable[table] = [];
|
|
454
480
|
resultsByTable[table].push(res);
|
|
455
481
|
}
|
|
@@ -461,7 +487,7 @@ var MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
|
461
487
|
const finalSorted = balancedResults.sort((a, b) => b.score - a.score);
|
|
462
488
|
if (finalSorted.length > 0) {
|
|
463
489
|
console.log(`[MultiTablePostgresProvider] --- Search Complete ---`);
|
|
464
|
-
console.log(`[MultiTablePostgresProvider] Final top match from "${(
|
|
490
|
+
console.log(`[MultiTablePostgresProvider] Final top match from "${(_c = (_b = finalSorted[0].metadata) == null ? void 0 : _b.source_table) != null ? _c : "unknown"}" with score ${finalSorted[0].score.toFixed(4)}`);
|
|
465
491
|
}
|
|
466
492
|
return finalSorted.slice(0, Math.max(topK, 15));
|
|
467
493
|
}
|
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.5",
|
|
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/core/Pipeline.ts
CHANGED
|
@@ -135,7 +135,8 @@ export class Pipeline {
|
|
|
135
135
|
|
|
136
136
|
try {
|
|
137
137
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: 'query' });
|
|
138
|
-
|
|
138
|
+
// 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, { __queryText: question });
|
|
139
140
|
|
|
140
141
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
141
142
|
const context = sources.length
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
import { Pool, PoolClient } from 'pg';
|
|
2
5
|
import { BaseVectorProvider } from './BaseVectorProvider';
|
|
3
6
|
import { VectorMatch, UpsertDocument } from '../../types';
|
|
@@ -46,7 +49,7 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
46
49
|
|
|
47
50
|
async initialize(): Promise<void> {
|
|
48
51
|
this.pool = new Pool({ connectionString: this.connectionString });
|
|
49
|
-
|
|
52
|
+
|
|
50
53
|
// Dynamically discover all tables that have an 'embedding' column
|
|
51
54
|
const client: PoolClient = await this.pool.connect();
|
|
52
55
|
try {
|
|
@@ -56,9 +59,9 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
56
59
|
WHERE column_name = 'embedding'
|
|
57
60
|
AND table_schema = 'public'
|
|
58
61
|
`);
|
|
59
|
-
|
|
62
|
+
|
|
60
63
|
const discoveredTables = res.rows.map(r => r.table_name);
|
|
61
|
-
|
|
64
|
+
|
|
62
65
|
if (this.tables.length === 0) {
|
|
63
66
|
// No static tables configured, use everything discovered
|
|
64
67
|
this.tables = discoveredTables;
|
|
@@ -66,7 +69,7 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
66
69
|
// Filter configured tables to ensure they actually exist with embeddings
|
|
67
70
|
const staticTables = [...this.tables];
|
|
68
71
|
this.tables = staticTables.filter(t => discoveredTables.includes(t));
|
|
69
|
-
|
|
72
|
+
|
|
70
73
|
if (this.tables.length < staticTables.length) {
|
|
71
74
|
const missing = staticTables.filter(t => !discoveredTables.includes(t));
|
|
72
75
|
console.warn(`[MultiTablePostgresProvider] Some configured tables were skipped (missing 'embedding' column): ${missing.join(', ')}`);
|
|
@@ -111,8 +114,8 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
111
114
|
async query(
|
|
112
115
|
vector: number[],
|
|
113
116
|
topK: number,
|
|
114
|
-
_namespace?: string,
|
|
115
|
-
_filter?: Record<string, unknown>
|
|
117
|
+
_namespace?: string,
|
|
118
|
+
_filter?: Record<string, unknown>
|
|
116
119
|
): Promise<VectorMatch[]> {
|
|
117
120
|
if (!this.pool) {
|
|
118
121
|
throw new Error('[MultiTablePostgresProvider] Provider not initialized. Call initialize() first.');
|
|
@@ -123,49 +126,78 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
123
126
|
|
|
124
127
|
console.log(`[MultiTablePostgresProvider] --- Starting Multi-Table Search ---`);
|
|
125
128
|
|
|
126
|
-
|
|
129
|
+
const queryText = _filter?.__queryText as string | undefined;
|
|
130
|
+
|
|
131
|
+
const queryPromises = this.tables.map(async (table) => {
|
|
127
132
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
let sqlQuery = '';
|
|
134
|
+
let params: unknown[] = [];
|
|
135
|
+
|
|
136
|
+
if (queryText) {
|
|
137
|
+
// Actual Hybrid Search: Semantic Vector Score + Full-Text Keyword Rank
|
|
138
|
+
sqlQuery = `
|
|
139
|
+
SELECT *,
|
|
140
|
+
(1 - (embedding <=> $1::vector)) AS vector_score,
|
|
141
|
+
ts_rank(to_tsvector('english', t.*::text), plainto_tsquery('english', $2)) AS keyword_score,
|
|
142
|
+
((1 - (embedding <=> $1::vector)) + (ts_rank(to_tsvector('english', t.*::text), plainto_tsquery('english', $2)) * 2.0)) AS hybrid_score
|
|
143
|
+
FROM "${table}" t
|
|
144
|
+
ORDER BY hybrid_score DESC
|
|
145
|
+
LIMIT 50
|
|
146
|
+
`;
|
|
147
|
+
params = [vectorLiteral, queryText];
|
|
148
|
+
} else {
|
|
149
|
+
// Fallback to Vector-only Search
|
|
150
|
+
sqlQuery = `
|
|
151
|
+
SELECT *,
|
|
152
|
+
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
153
|
+
FROM "${table}" t
|
|
154
|
+
ORDER BY hybrid_score DESC
|
|
155
|
+
LIMIT 50
|
|
156
|
+
`;
|
|
157
|
+
params = [vectorLiteral];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const result = await this.pool.query(sqlQuery, params);
|
|
139
161
|
|
|
140
162
|
if (result.rowCount && result.rowCount > 0) {
|
|
141
|
-
console.log(` ✅ Table "${table}": Found ${result.rowCount} potential matches. Top score: ${result.rows[0].
|
|
163
|
+
console.log(` ✅ Table "${table}": Found ${result.rowCount} potential matches. Top score: ${result.rows[0].hybrid_score.toFixed(4)}`);
|
|
142
164
|
} else {
|
|
143
165
|
console.log(` ⚪ Table "${table}": No relevant matches found.`);
|
|
144
166
|
}
|
|
145
167
|
|
|
168
|
+
const tableResults: VectorMatch[] = [];
|
|
146
169
|
for (const row of result.rows) {
|
|
147
|
-
const {
|
|
170
|
+
const { hybrid_score, id, ...rest } = row as Record<string, unknown>;
|
|
148
171
|
delete rest.embedding;
|
|
172
|
+
delete rest.vector_score;
|
|
173
|
+
delete rest.keyword_score;
|
|
149
174
|
|
|
150
|
-
const content = `[TYPE: ${table.replace(/s$/, '').toUpperCase()}]\n` +
|
|
175
|
+
const content = `[TYPE: ${table.replace(/s$/, '').toUpperCase()}]\n` +
|
|
151
176
|
Object.entries(rest)
|
|
152
177
|
.filter(([k, v]) => v !== null && typeof v !== 'object' && k !== 'id')
|
|
153
178
|
.map(([k, v]) => `${k}: ${v}`)
|
|
154
179
|
.join('\n');
|
|
155
180
|
|
|
156
|
-
|
|
181
|
+
tableResults.push({
|
|
157
182
|
id: `${table}-${id}`,
|
|
158
|
-
score: parseFloat(String(
|
|
183
|
+
score: parseFloat(String(hybrid_score)),
|
|
159
184
|
content,
|
|
160
185
|
metadata: { ...rest, source_table: table },
|
|
161
186
|
});
|
|
162
187
|
}
|
|
188
|
+
return tableResults;
|
|
163
189
|
} catch (err) {
|
|
164
190
|
console.error(
|
|
165
191
|
`[MultiTablePostgresProvider] Error querying table "${table}":`,
|
|
166
192
|
(err as Error).message
|
|
167
193
|
);
|
|
194
|
+
return [];
|
|
168
195
|
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const resultsArray = await Promise.all(queryPromises);
|
|
199
|
+
for (const tableResults of resultsArray) {
|
|
200
|
+
allResults.push(...tableResults);
|
|
169
201
|
}
|
|
170
202
|
|
|
171
203
|
// 1. Group results by table to ensure we take the top ones from each
|
|
@@ -179,7 +211,7 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
179
211
|
// 2. Take top 3 from EACH table guaranteed, then fill the rest with overall best
|
|
180
212
|
const balancedResults: VectorMatch[] = [];
|
|
181
213
|
const tables = Object.keys(resultsByTable);
|
|
182
|
-
|
|
214
|
+
|
|
183
215
|
// First, ensure every table's best match is included
|
|
184
216
|
for (const table of tables) {
|
|
185
217
|
balancedResults.push(...resultsByTable[table].slice(0, 3));
|