@retrivora-ai/rag-engine 1.8.2 → 1.8.4

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.
Files changed (49) hide show
  1. package/dist/handlers/index.js +142 -16
  2. package/dist/handlers/index.mjs +5848 -17
  3. package/dist/index.css +3162 -0
  4. package/dist/index.d.mts +9 -15
  5. package/dist/index.d.ts +9 -15
  6. package/dist/index.js +1355 -919
  7. package/dist/index.mjs +1437 -896
  8. package/dist/server.d.mts +6 -0
  9. package/dist/server.d.ts +6 -0
  10. package/dist/server.js +171 -17
  11. package/dist/server.mjs +5923 -68
  12. package/package.json +8 -6
  13. package/src/app/globals.css +35 -11
  14. package/src/components/ChatWidget.tsx +0 -1
  15. package/src/components/MarkdownComponents.tsx +3 -0
  16. package/src/components/MessageBubble.tsx +3 -2
  17. package/src/components/ObservabilityPanel.tsx +1 -1
  18. package/src/components/UIDispatcher.tsx +0 -3
  19. package/src/config/ConfigBuilder.ts +38 -1
  20. package/src/core/LangChainAgent.ts +1 -4
  21. package/src/core/Pipeline.ts +31 -18
  22. package/src/core/QueryProcessor.ts +65 -0
  23. package/src/rag/Reranker.ts +99 -6
  24. package/src/tailwind.css +2 -0
  25. package/src/utils/ProductExtractor.ts +3 -3
  26. package/dist/ChromaDBProvider-MIDOR4FW.mjs +0 -8
  27. package/dist/MilvusProvider-U7SKC27V.mjs +0 -8
  28. package/dist/MongoDBProvider-YNKC7EJ6.mjs +0 -8
  29. package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +0 -8
  30. package/dist/PineconeProvider-QZNRKTN2.mjs +0 -8
  31. package/dist/QdrantProvider-RLJTNGPY.mjs +0 -8
  32. package/dist/RedisProvider-SR65SCKV.mjs +0 -8
  33. package/dist/SimpleGraphProvider-SLOXO4M7.mjs +0 -62
  34. package/dist/UniversalVectorProvider-IN67OS56.mjs +0 -9
  35. package/dist/WeaviateProvider-5FWDFITI.mjs +0 -8
  36. package/dist/chunk-5AJ4XHLW.mjs +0 -201
  37. package/dist/chunk-5YGUXK7Z.mjs +0 -80
  38. package/dist/chunk-CFVEZTBJ.mjs +0 -102
  39. package/dist/chunk-ICKRMZQK.mjs +0 -76
  40. package/dist/chunk-IMP6FUCY.mjs +0 -30
  41. package/dist/chunk-LR3VMDVK.mjs +0 -157
  42. package/dist/chunk-LZVVLSDN.mjs +0 -4077
  43. package/dist/chunk-M6JSPGAR.mjs +0 -117
  44. package/dist/chunk-OZFBG4BA.mjs +0 -291
  45. package/dist/chunk-PSFPZXHX.mjs +0 -245
  46. package/dist/chunk-U55XRW3U.mjs +0 -96
  47. package/dist/chunk-VUQJVIJT.mjs +0 -148
  48. package/dist/chunk-X4TOT24V.mjs +0 -89
  49. package/dist/chunk-YLTMFW4M.mjs +0 -49
@@ -1,117 +0,0 @@
1
- import {
2
- BaseVectorProvider
3
- } from "./chunk-IMP6FUCY.mjs";
4
- import {
5
- __spreadValues
6
- } from "./chunk-X4TOT24V.mjs";
7
-
8
- // src/providers/vectordb/WeaviateProvider.ts
9
- import axios from "axios";
10
- var WeaviateProvider = class extends BaseVectorProvider {
11
- constructor(config) {
12
- super(config);
13
- const opts = config.options;
14
- const baseUrl = opts.baseUrl || opts.url;
15
- if (!baseUrl) throw new Error("[WeaviateProvider] options.baseUrl is required");
16
- this.http = axios.create({
17
- baseURL: baseUrl,
18
- headers: __spreadValues({
19
- "Content-Type": "application/json"
20
- }, opts.apiKey ? {
21
- "X-OpenAI-Api-Key": opts.apiKey,
22
- "Authorization": `Bearer ${opts.apiKey}`
23
- } : {})
24
- });
25
- }
26
- async initialize() {
27
- await this.ping();
28
- }
29
- async upsert(doc, namespace) {
30
- const payload = {
31
- class: this.indexName,
32
- id: doc.id,
33
- vector: doc.vector,
34
- properties: {
35
- content: doc.content,
36
- metadata: JSON.stringify(doc.metadata || {}),
37
- namespace: namespace || ""
38
- }
39
- };
40
- await this.http.post("/v1/objects", payload);
41
- }
42
- async batchUpsert(docs, namespace) {
43
- const payload = {
44
- objects: docs.map((doc) => ({
45
- class: this.indexName,
46
- id: doc.id,
47
- vector: doc.vector,
48
- properties: {
49
- content: doc.content,
50
- metadata: JSON.stringify(doc.metadata || {}),
51
- namespace: namespace || ""
52
- }
53
- }))
54
- };
55
- await this.http.post("/v1/batch/objects", payload);
56
- }
57
- async query(vector, topK, namespace, _filter) {
58
- var _a, _b;
59
- const sanitizedFilter = this.sanitizeFilter(_filter);
60
- const queryText = sanitizedFilter.queryText;
61
- const searchParams = queryText ? `hybrid: { query: ${JSON.stringify(queryText)}, alpha: 0.5 }` : `nearVector: { vector: ${JSON.stringify(vector)} }`;
62
- const graphqlQuery = {
63
- query: `
64
- {
65
- Get {
66
- ${this.indexName}(
67
- ${searchParams}
68
- limit: ${topK}
69
- ${namespace ? `where: { path: ["namespace"], operator: Equal, valueString: "${namespace}" }` : ""}
70
- ) {
71
- content
72
- metadata
73
- _additional {
74
- id
75
- distance
76
- }
77
- }
78
- }
79
- }
80
- `
81
- };
82
- const { data } = await this.http.post("/v1/graphql", graphqlQuery);
83
- const results = ((_b = (_a = data.data) == null ? void 0 : _a.Get) == null ? void 0 : _b[this.indexName]) || [];
84
- return results.map((res) => ({
85
- id: res["_additional"].id,
86
- score: 1 - res["_additional"].distance,
87
- content: res["content"],
88
- metadata: res["metadata"] ? JSON.parse(res["metadata"]) : {}
89
- }));
90
- }
91
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
92
- async delete(id, _namespace) {
93
- await this.http.delete(`/v1/objects/${this.indexName}/${id}`);
94
- }
95
- async deleteNamespace(namespace) {
96
- await this.http.post("/v1/batch/objects", {
97
- match: {
98
- class: this.indexName,
99
- where: { path: ["namespace"], operator: "Equal", valueString: namespace }
100
- }
101
- });
102
- }
103
- async ping() {
104
- try {
105
- await this.http.get("/v1/.well-known/live");
106
- return true;
107
- } catch (e) {
108
- return false;
109
- }
110
- }
111
- async disconnect() {
112
- }
113
- };
114
-
115
- export {
116
- WeaviateProvider
117
- };
@@ -1,291 +0,0 @@
1
- import {
2
- BaseVectorProvider
3
- } from "./chunk-IMP6FUCY.mjs";
4
- import {
5
- __objRest,
6
- __spreadProps,
7
- __spreadValues
8
- } from "./chunk-X4TOT24V.mjs";
9
-
10
- // src/providers/vectordb/MultiTablePostgresProvider.ts
11
- import { Pool } from "pg";
12
- var MultiTablePostgresProvider = class extends BaseVectorProvider {
13
- constructor(config) {
14
- var _a, _b, _c;
15
- super(config);
16
- const opts = config.options || {};
17
- if (!opts.connectionString) {
18
- throw new Error("[MultiTablePostgresProvider] options.connectionString is required");
19
- }
20
- this.connectionString = opts.connectionString;
21
- this.dimensions = (_a = opts.dimensions) != null ? _a : 768;
22
- this.tables = [];
23
- const rawSearchFields = (_c = (_b = opts.searchFields) != null ? _b : process.env.VECTOR_DB_SEARCH_FIELDS) != null ? _c : ["name", "product_name", "productname", "title"];
24
- this.searchFields = typeof rawSearchFields === "string" ? rawSearchFields.split(",").map((f) => f.trim()).filter(Boolean) : rawSearchFields;
25
- this.uploadTable = opts.uploadTable || "document_chunks";
26
- }
27
- async initialize() {
28
- this.pool = new Pool({ connectionString: this.connectionString });
29
- const client = await this.pool.connect();
30
- try {
31
- await client.query("CREATE EXTENSION IF NOT EXISTS vector");
32
- await client.query(`
33
- CREATE TABLE IF NOT EXISTS ${this.uploadTable} (
34
- id TEXT PRIMARY KEY,
35
- namespace TEXT NOT NULL DEFAULT '',
36
- content TEXT NOT NULL,
37
- metadata JSONB,
38
- embedding VECTOR(${this.dimensions})
39
- )
40
- `);
41
- await client.query(`
42
- CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
43
- ON ${this.uploadTable}
44
- USING hnsw (embedding vector_cosine_ops)
45
- `);
46
- const res = await client.query(`
47
- SELECT table_name
48
- FROM information_schema.columns
49
- WHERE column_name = 'embedding'
50
- AND table_schema = 'public'
51
- `);
52
- this.tables = res.rows.map((r) => r.table_name);
53
- if (this.tables.length === 0) {
54
- console.warn('[MultiTablePostgresProvider] No tables with "embedding" columns found in the database.');
55
- } else {
56
- console.log(
57
- `[MultiTablePostgresProvider] Connected. Searching across ${this.tables.length} table(s): ${this.tables.join(", ")}`
58
- );
59
- }
60
- } finally {
61
- client.release();
62
- }
63
- }
64
- /**
65
- * Upsert a document by dynamically provisioning a table and columns.
66
- */
67
- async upsert(doc, namespace = "") {
68
- await this.batchUpsert([doc], namespace);
69
- }
70
- /**
71
- * Batch upsert documents by dynamically provisioning tables and columns based on CSV headers.
72
- */
73
- async batchUpsert(docs, namespace = "") {
74
- var _a;
75
- if (docs.length === 0) return;
76
- const docsByFile = {};
77
- for (const doc of docs) {
78
- const fileName = ((_a = doc.metadata) == null ? void 0 : _a.fileName) || this.uploadTable;
79
- if (!docsByFile[fileName]) docsByFile[fileName] = [];
80
- docsByFile[fileName].push(doc);
81
- }
82
- const client = await this.pool.connect();
83
- try {
84
- await client.query("BEGIN");
85
- for (const [fileName, fileDocs] of Object.entries(docsByFile)) {
86
- let tableName = fileName;
87
- if (tableName.toLowerCase().endsWith(".csv")) {
88
- tableName = tableName.slice(0, -4);
89
- }
90
- tableName = tableName.replace(/[^a-z0-9_]/gi, "_").toLowerCase() || this.uploadTable;
91
- const firstMeta = fileDocs[0].metadata || {};
92
- const systemKeys = ["fileName", "fileSize", "fileType", "uploadedAt", "dimension", "chunkIndex", "id", "namespace", "content", "metadata", "embedding"];
93
- const csvHeaders = Object.keys(firstMeta).filter((k) => !systemKeys.includes(k) && !systemKeys.includes(k.toLowerCase()));
94
- const columnDefs = csvHeaders.map((h) => `"${h}" TEXT`).join(",\n ");
95
- const createTableSql = `
96
- CREATE TABLE IF NOT EXISTS "${tableName}" (
97
- id TEXT PRIMARY KEY,
98
- ${columnDefs ? columnDefs + "," : ""}
99
- namespace TEXT NOT NULL DEFAULT '',
100
- content TEXT NOT NULL,
101
- metadata JSONB,
102
- embedding VECTOR(${this.dimensions})
103
- )
104
- `;
105
- await client.query(createTableSql);
106
- await client.query(`
107
- CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
108
- ON "${tableName}"
109
- USING hnsw (embedding vector_cosine_ops)
110
- `);
111
- if (!this.tables.includes(tableName)) {
112
- this.tables.push(tableName);
113
- }
114
- const BATCH_SIZE = 50;
115
- for (let i = 0; i < fileDocs.length; i += BATCH_SIZE) {
116
- const batch = fileDocs.slice(i, i + BATCH_SIZE);
117
- const values = [];
118
- const headerColumns = csvHeaders.map((h) => `"${h}"`).join(", ");
119
- const allColumns = `id, ${headerColumns ? headerColumns + ", " : ""}namespace, content, metadata, embedding`;
120
- const valuePlaceholders = batch.map((doc, idx) => {
121
- var _a2, _b;
122
- const offset = idx * (5 + csvHeaders.length);
123
- values.push(doc.id);
124
- for (const h of csvHeaders) {
125
- values.push(String(((_a2 = doc.metadata) == null ? void 0 : _a2[h]) || ""));
126
- }
127
- values.push(namespace, doc.content, JSON.stringify((_b = doc.metadata) != null ? _b : {}), `[${doc.vector.join(",")}]`);
128
- const docPlaceholders = [];
129
- for (let j = 1; j <= 5 + csvHeaders.length; j++) {
130
- if (j === 5 + csvHeaders.length) {
131
- docPlaceholders.push(`$${offset + j}::vector`);
132
- } else {
133
- docPlaceholders.push(`$${offset + j}`);
134
- }
135
- }
136
- return `(${docPlaceholders.join(", ")})`;
137
- }).join(", ");
138
- const conflictUpdates = csvHeaders.map((h) => `"${h}" = EXCLUDED."${h}"`).join(",\n ");
139
- const query = `
140
- INSERT INTO "${tableName}" (${allColumns})
141
- VALUES ${valuePlaceholders}
142
- ON CONFLICT (id) DO UPDATE
143
- SET ${conflictUpdates ? conflictUpdates + "," : ""}
144
- namespace = EXCLUDED.namespace,
145
- content = EXCLUDED.content,
146
- metadata = EXCLUDED.metadata,
147
- embedding = EXCLUDED.embedding
148
- `;
149
- await client.query(query, values);
150
- }
151
- }
152
- await client.query("COMMIT");
153
- } catch (error) {
154
- await client.query("ROLLBACK");
155
- throw error;
156
- } finally {
157
- client.release();
158
- }
159
- }
160
- /**
161
- * Query all configured tables and merge results, sorted by cosine similarity score.
162
- */
163
- async query(vector, topK, _namespace, _filter) {
164
- var _a;
165
- if (!this.pool) {
166
- throw new Error("[MultiTablePostgresProvider] Provider not initialized. Call initialize() first.");
167
- }
168
- const vectorLiteral = `[${vector.join(",")}]`;
169
- const allResults = [];
170
- console.log(`[MultiTablePostgresProvider] --- Starting Multi-Table Search ---`);
171
- const queryText = _filter == null ? void 0 : _filter.queryText;
172
- const entityHints = Array.isArray(_filter == null ? void 0 : _filter.__entityHints) ? _filter.__entityHints.filter(
173
- (hint) => typeof hint === "object" && hint !== null && typeof hint.value === "string"
174
- ).map((hint) => hint.value.trim().toLowerCase()).filter(Boolean) : [];
175
- console.log(`[MultiTablePostgresProvider] queryText: "${queryText}"`);
176
- console.log(`[MultiTablePostgresProvider] entityHints: [${entityHints.join(", ")}]`);
177
- const getDynamicKeywordQuery = () => {
178
- if (entityHints.length > 0) {
179
- return entityHints.map((h) => h.includes(" ") ? `"${h}"` : h).join(" & ");
180
- }
181
- if (queryText) {
182
- return queryText.toLowerCase().replace(/\b(show|me|the|product[s]?|item[s]?|card[s]?|of|category|find|list|browse|what|are|display|all|under|for|in|with|about)\b/g, "").trim().replace(/\s+/g, " & ");
183
- }
184
- return "";
185
- };
186
- const dynamicKeywordQuery = getDynamicKeywordQuery();
187
- const queryPromises = this.tables.map(async (table) => {
188
- try {
189
- let sqlQuery = "";
190
- let params = [];
191
- if (queryText) {
192
- const hasEntityHints = entityHints.length > 0;
193
- const exactNameScoreExpr = hasEntityHints ? `+ (
194
- SELECT COALESCE(MAX(
195
- CASE WHEN LOWER(val) IN (${entityHints.map((h) => `'${h.replace(/'/g, "''")}'`).join(", ")})
196
- THEN 1.0 ELSE 0.0 END
197
- ), 0)
198
- FROM jsonb_each_text(to_jsonb(t)) AS kv(key, val)
199
- WHERE key IN (${this.searchFields.map((f) => `'${f}'`).join(", ")})
200
- ) * 3.0` : "";
201
- sqlQuery = `
202
- SELECT *,
203
- (1 - (embedding <=> $1::vector)) AS vector_score,
204
- COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) AS keyword_score,
205
- ((1 - (embedding <=> $1::vector)) + (COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) * 2.0) ${exactNameScoreExpr}) AS hybrid_score
206
- FROM "${table}" t
207
- ORDER BY hybrid_score DESC
208
- LIMIT 50
209
- `;
210
- params = [vectorLiteral, dynamicKeywordQuery];
211
- } else {
212
- sqlQuery = `
213
- SELECT *,
214
- (1 - (embedding <=> $1::vector)) AS hybrid_score
215
- FROM "${table}" t
216
- ORDER BY hybrid_score DESC
217
- LIMIT 50
218
- `;
219
- params = [vectorLiteral];
220
- }
221
- const result = await this.pool.query(sqlQuery, params);
222
- if (result.rowCount && result.rowCount > 0) {
223
- console.log(` \u2705 Table "${table}": Found ${result.rowCount} potential matches. Top score: ${result.rows[0].hybrid_score.toFixed(4)}`);
224
- } else {
225
- console.log(` \u26AA Table "${table}": No relevant matches found.`);
226
- }
227
- const tableResults = [];
228
- for (const row of result.rows) {
229
- const _a2 = row, { hybrid_score, id } = _a2, rest = __objRest(_a2, ["hybrid_score", "id"]);
230
- delete rest.embedding;
231
- delete rest.vector_score;
232
- delete rest.keyword_score;
233
- delete rest.exact_name_score;
234
- const content = `[TYPE: ${table.replace(/s$/, "").toUpperCase()}]
235
- ` + Object.entries(rest).filter(([k, v]) => v !== null && typeof v !== "object" && k !== "id").map(([k, v]) => `${k}: ${v}`).join("\n");
236
- tableResults.push({
237
- id: `${table}-${id}`,
238
- score: parseFloat(String(hybrid_score)),
239
- content,
240
- metadata: __spreadProps(__spreadValues({}, rest), { source_table: table })
241
- });
242
- }
243
- return tableResults;
244
- } catch (err) {
245
- console.error(
246
- `[MultiTablePostgresProvider] Error querying table "${table}":`,
247
- err.message
248
- );
249
- return [];
250
- }
251
- });
252
- const resultsArray = await Promise.all(queryPromises);
253
- for (const tableResults of resultsArray) {
254
- allResults.push(...tableResults);
255
- }
256
- allResults.sort((a, b) => b.score - a.score);
257
- if (allResults.length === 0) return [];
258
- const bestMatchTable = (_a = allResults[0].metadata) == null ? void 0 : _a.source_table;
259
- const finalSorted = allResults.filter((res) => {
260
- var _a2;
261
- return ((_a2 = res.metadata) == null ? void 0 : _a2.source_table) === bestMatchTable;
262
- });
263
- console.log(`[MultiTablePostgresProvider] --- Search Complete ---`);
264
- console.log(`[MultiTablePostgresProvider] Final top match from "${bestMatchTable}" with score ${finalSorted[0].score.toFixed(4)}`);
265
- console.log(`[MultiTablePostgresProvider] Restricting recommendations strictly to table: "${bestMatchTable}"`);
266
- return finalSorted.slice(0, Math.max(topK, 15));
267
- }
268
- async delete(_id, _namespace) {
269
- console.warn("[MultiTablePostgresProvider] delete() is a no-op for multi-table mode.");
270
- }
271
- async deleteNamespace(_namespace) {
272
- console.warn("[MultiTablePostgresProvider] deleteNamespace() is a no-op for multi-table mode.");
273
- }
274
- async ping() {
275
- try {
276
- await this.pool.query("SELECT 1");
277
- return true;
278
- } catch (e) {
279
- return false;
280
- }
281
- }
282
- async disconnect() {
283
- if (this.pool) {
284
- await this.pool.end();
285
- }
286
- }
287
- };
288
-
289
- export {
290
- MultiTablePostgresProvider
291
- };
@@ -1,245 +0,0 @@
1
- import {
2
- BaseVectorProvider
3
- } from "./chunk-IMP6FUCY.mjs";
4
- import {
5
- __spreadValues
6
- } from "./chunk-X4TOT24V.mjs";
7
-
8
- // src/providers/vectordb/QdrantProvider.ts
9
- import axios from "axios";
10
- import crypto from "crypto";
11
- var QdrantProvider = class extends BaseVectorProvider {
12
- constructor(config) {
13
- super(config);
14
- this.schemaDiscovered = false;
15
- const opts = config.options;
16
- const baseUrl = opts.baseUrl;
17
- if (!baseUrl) throw new Error("[QdrantProvider] baseUrl is required");
18
- this.contentField = opts.contentField || "content";
19
- this.metadataField = opts.metadataField || "metadata";
20
- this.isFlatPayload = !!opts.flatPayload;
21
- this.http = axios.create({
22
- baseURL: baseUrl,
23
- timeout: 15e3,
24
- // 15s timeout for vector DB operations
25
- headers: __spreadValues({
26
- "Content-Type": "application/json"
27
- }, opts.apiKey ? { "api-key": opts.apiKey } : {})
28
- });
29
- }
30
- async initialize() {
31
- if (this.schemaDiscovered) return;
32
- await this.ping();
33
- await this.ensureCollection();
34
- console.log(`[QdrantProvider] \u{1F50D} Discovering schema for collection "${this.indexName}"...`);
35
- const discoveredFields = await this.discoverSchema();
36
- const opts = this.config.options;
37
- const configFields = opts.filterableFields || [];
38
- const allFields = [.../* @__PURE__ */ new Set([...discoveredFields, ...configFields])];
39
- if (allFields.length > 0) {
40
- console.log(`[QdrantProvider] \u{1F6E0} Ensuring indexes for ${allFields.length} discovered fields...`);
41
- await Promise.all(
42
- allFields.map(async (fieldInfo) => {
43
- const [fieldName, schemaType] = fieldInfo.split(":");
44
- return this.ensureIndex(fieldName, schemaType || "keyword");
45
- })
46
- );
47
- } else {
48
- console.log(`[QdrantProvider] \u2139\uFE0F No fields discovered for indexing.`);
49
- }
50
- this.schemaDiscovered = true;
51
- }
52
- /**
53
- * Samples points from the collection to discover available payload fields.
54
- */
55
- async discoverSchema() {
56
- var _a;
57
- try {
58
- const { data } = await this.http.post(`/collections/${this.indexName}/points/scroll`, {
59
- limit: 20,
60
- with_payload: true,
61
- with_vector: false
62
- });
63
- const points = ((_a = data.result) == null ? void 0 : _a.points) || [];
64
- const keys = /* @__PURE__ */ new Set();
65
- for (const point of points) {
66
- const payload = point.payload || {};
67
- Object.keys(payload).forEach((k) => {
68
- if (k !== "namespace" && k !== this.contentField && k !== this.metadataField) {
69
- keys.add(k);
70
- }
71
- });
72
- if (!this.isFlatPayload && payload[this.metadataField]) {
73
- const meta = payload[this.metadataField];
74
- if (typeof meta === "object" && meta !== null) {
75
- Object.keys(meta).forEach((k) => keys.add(k));
76
- }
77
- }
78
- }
79
- return Array.from(keys);
80
- } catch (err) {
81
- console.warn(`[QdrantProvider] \u26A0\uFE0F Schema discovery failed:`, err instanceof Error ? err.message : String(err));
82
- return [];
83
- }
84
- }
85
- /**
86
- * Ensures the collection exists. Creates it if missing.
87
- */
88
- async ensureCollection() {
89
- var _a;
90
- try {
91
- await this.http.get(`/collections/${this.indexName}`);
92
- console.log(`[QdrantProvider] \u2705 Collection "${this.indexName}" already exists.`);
93
- } catch (err) {
94
- if (axios.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
95
- const opts = this.config.options;
96
- const dimensionsForCreate = opts.dimensions || 1536;
97
- console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
98
- await this.http.put(`/collections/${this.indexName}`, {
99
- vectors: {
100
- size: dimensionsForCreate,
101
- distance: "Cosine"
102
- }
103
- });
104
- console.log(`[QdrantProvider] \u2705 Created collection "${this.indexName}"`);
105
- } else {
106
- throw err;
107
- }
108
- }
109
- }
110
- /**
111
- * Ensures that a payload field has an index.
112
- */
113
- async ensureIndex(fieldName, schema = "keyword") {
114
- var _a;
115
- let fullPath = fieldName;
116
- if (!this.isFlatPayload && !fieldName.includes(".") && fieldName !== "namespace" && fieldName !== this.contentField) {
117
- fullPath = `${this.metadataField}.${fieldName}`;
118
- }
119
- try {
120
- await this.http.put(`/collections/${this.indexName}/index`, {
121
- field_name: fullPath,
122
- field_schema: schema
123
- });
124
- console.log(`[QdrantProvider] \u2705 Ensured ${schema} index for "${fullPath}"`);
125
- } catch (err) {
126
- let status;
127
- if (axios.isAxiosError(err)) status = (_a = err.response) == null ? void 0 : _a.status;
128
- if (status === 409) return;
129
- console.warn(`[QdrantProvider] \u26A0\uFE0F Could not ensure index for "${fullPath}":`, err instanceof Error ? err.message : String(err));
130
- }
131
- }
132
- async upsert(doc, namespace) {
133
- await this.batchUpsert([doc], namespace);
134
- }
135
- async batchUpsert(docs, namespace) {
136
- const payload = {
137
- points: docs.map((doc) => {
138
- const pointPayload = __spreadValues({
139
- [this.contentField]: doc.content
140
- }, namespace ? { namespace } : {});
141
- if (this.isFlatPayload) {
142
- Object.assign(pointPayload, doc.metadata || {});
143
- } else {
144
- pointPayload[this.metadataField] = doc.metadata || {};
145
- }
146
- return {
147
- id: this.normalizeId(doc.id),
148
- vector: doc.vector,
149
- payload: pointPayload
150
- };
151
- })
152
- };
153
- await this.http.put(`/collections/${this.indexName}/points`, payload);
154
- }
155
- async query(vector, topK, namespace, _filter) {
156
- const must = [];
157
- if (namespace) {
158
- must.push({ key: "namespace", match: { value: namespace } });
159
- }
160
- const sanitizedFilter = this.sanitizeFilter(_filter);
161
- for (const [key, value] of Object.entries(sanitizedFilter)) {
162
- let filterKey = key;
163
- if (!this.isFlatPayload && !key.includes(".") && key !== "namespace" && key !== this.contentField) {
164
- filterKey = `${this.metadataField}.${key}`;
165
- }
166
- if (Array.isArray(value)) {
167
- must.push({ key: filterKey, match: { any: value } });
168
- } else {
169
- must.push({ key: filterKey, match: { value } });
170
- }
171
- }
172
- const payload = {
173
- vector,
174
- limit: topK,
175
- with_payload: true,
176
- params: {
177
- hnsw_ef: this.config.options.efSearch || Math.max(topK * 20, 128),
178
- exact: false
179
- },
180
- filter: must.length > 0 ? { must } : void 0
181
- };
182
- const { data } = await this.http.post(`/collections/${this.indexName}/points/search`, payload);
183
- const results = (data.result || []).map((res) => {
184
- const p = res.payload || {};
185
- let content = p[this.contentField] || "";
186
- if (!content) {
187
- const stringFields = Object.entries(p).filter(([, v]) => typeof v === "string").map(([k, v]) => ({ key: k, val: v }));
188
- if (stringFields.length > 0) {
189
- const bestMatch = stringFields.sort((a, b) => b.val.length - a.val.length)[0];
190
- content = bestMatch.val;
191
- } else {
192
- content = JSON.stringify(p);
193
- }
194
- }
195
- let metadata = {};
196
- if (this.isFlatPayload) {
197
- metadata = __spreadValues({}, p);
198
- delete metadata[this.contentField];
199
- delete metadata["namespace"];
200
- } else {
201
- metadata = p[this.metadataField] || p;
202
- }
203
- return {
204
- id: res.id,
205
- score: res.score,
206
- content,
207
- metadata
208
- };
209
- });
210
- return results;
211
- }
212
- async delete(id) {
213
- await this.http.post(`/collections/${this.indexName}/points/delete`, {
214
- points: [this.normalizeId(id)]
215
- });
216
- }
217
- async deleteNamespace(_namespace) {
218
- await this.http.post(`/collections/${this.indexName}/points/delete`, {
219
- filter: {
220
- must: [{ key: "namespace", match: { value: _namespace } }]
221
- }
222
- });
223
- }
224
- async ping() {
225
- try {
226
- await this.http.get("/healthz");
227
- return true;
228
- } catch (e) {
229
- return false;
230
- }
231
- }
232
- normalizeId(id) {
233
- if (typeof id === "number") return id;
234
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
235
- if (uuidRegex.test(id)) return id.toLowerCase();
236
- const hash = crypto.createHash("md5").update(id).digest("hex");
237
- return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-${hash.slice(12, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
238
- }
239
- async disconnect() {
240
- }
241
- };
242
-
243
- export {
244
- QdrantProvider
245
- };