@oracle/langchain-oracledb 0.1.1

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/dist/document_loaders.cjs +211 -0
  4. package/dist/document_loaders.cjs.map +1 -0
  5. package/dist/document_loaders.d.cts +33 -0
  6. package/dist/document_loaders.d.ts +33 -0
  7. package/dist/document_loaders.js +181 -0
  8. package/dist/document_loaders.js.map +1 -0
  9. package/dist/embeddings.cjs +129 -0
  10. package/dist/embeddings.cjs.map +1 -0
  11. package/dist/embeddings.d.cts +35 -0
  12. package/dist/embeddings.d.ts +35 -0
  13. package/dist/embeddings.js +122 -0
  14. package/dist/embeddings.js.map +1 -0
  15. package/dist/index.cjs +22 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +5 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.js +6 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/summary.cjs +63 -0
  22. package/dist/summary.cjs.map +1 -0
  23. package/dist/summary.d.cts +16 -0
  24. package/dist/summary.d.ts +16 -0
  25. package/dist/summary.js +56 -0
  26. package/dist/summary.js.map +1 -0
  27. package/dist/text_splitter.cjs +58 -0
  28. package/dist/text_splitter.cjs.map +1 -0
  29. package/dist/text_splitter.d.cts +17 -0
  30. package/dist/text_splitter.d.ts +17 -0
  31. package/dist/text_splitter.js +51 -0
  32. package/dist/text_splitter.js.map +1 -0
  33. package/dist/vectorstores.cjs +658 -0
  34. package/dist/vectorstores.cjs.map +1 -0
  35. package/dist/vectorstores.d.cts +98 -0
  36. package/dist/vectorstores.d.ts +98 -0
  37. package/dist/vectorstores.js +647 -0
  38. package/dist/vectorstores.js.map +1 -0
  39. package/index.cjs +1 -0
  40. package/index.d.cts +1 -0
  41. package/index.d.ts +1 -0
  42. package/index.js +1 -0
  43. package/package.json +88 -0
@@ -0,0 +1,658 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OracleVS = exports.dropTablePurge = exports.createIndex = exports.createTable = exports.DistanceStrategy = exports.generateWhereClause = void 0;
7
+ const oracledb_1 = __importDefault(require("oracledb"));
8
+ const crypto_1 = require("crypto");
9
+ const vectorstores_1 = require("@langchain/core/vectorstores");
10
+ const documents_1 = require("@langchain/core/documents");
11
+ const math_1 = require("@langchain/core/utils/math");
12
+ function generateWhereClause(dbFilter, bindValues) {
13
+ // Handle $and
14
+ if ("$and" in dbFilter && Array.isArray(dbFilter.$and)) {
15
+ const andConditions = dbFilter.$and.map((cond) => generateWhereClause(cond, bindValues));
16
+ return `(${andConditions.join(" AND ")})`;
17
+ }
18
+ // Handle $or
19
+ if ("$or" in dbFilter && Array.isArray(dbFilter.$or)) {
20
+ const orConditions = dbFilter.$or.map((cond) => generateWhereClause(cond, bindValues));
21
+ return `(${orConditions.join(" OR ")})`;
22
+ }
23
+ // Otherwise, normal object with key: condition(s)
24
+ const conditions = [];
25
+ for (const [col, val] of Object.entries(dbFilter)) {
26
+ if (typeof val === "object" && val !== null && !Array.isArray(val)) {
27
+ // Operator-based filters ($eq, $lte, $in,...)
28
+ for (const [op, operand] of Object.entries(val)) {
29
+ conditions.push(generateOperatorCondition(col, op, operand, bindValues));
30
+ }
31
+ }
32
+ else if (Array.isArray(val)) {
33
+ // shorthand { tags: ["a","b"] } → IN
34
+ conditions.push(generateOperatorCondition(col, "$in", val, bindValues));
35
+ }
36
+ else {
37
+ // shorthand { name: "John" } → equality
38
+ conditions.push(generateOperatorCondition(col, "$eq", val, bindValues));
39
+ }
40
+ }
41
+ return conditions.length > 1
42
+ ? `(${conditions.join(" AND ")})`
43
+ : conditions[0];
44
+ }
45
+ exports.generateWhereClause = generateWhereClause;
46
+ function generateOperatorCondition(column, operator, value, bindValues) {
47
+ switch (operator) {
48
+ case "$eq":
49
+ return jsonCompare(column, "=", value, bindValues);
50
+ case "$ne":
51
+ return jsonCompare(column, "!=", value, bindValues);
52
+ case "$lt":
53
+ return jsonCompare(column, "<", value, bindValues);
54
+ case "$lte":
55
+ return jsonCompare(column, "<=", value, bindValues);
56
+ case "$gt":
57
+ return jsonCompare(column, ">", value, bindValues);
58
+ case "$gte":
59
+ return jsonCompare(column, ">=", value, bindValues);
60
+ case "$in": {
61
+ if (!Array.isArray(value))
62
+ throw new Error("$in requires array");
63
+ const inClauses = value.map((v) => jsonCompare(column, "=", v, bindValues));
64
+ return `(${inClauses.join(" OR ")})`;
65
+ }
66
+ case "$nin": {
67
+ if (!Array.isArray(value))
68
+ throw new Error("$nin requires array");
69
+ const ninClauses = value.map((v) => jsonCompare(column, "!=", v, bindValues));
70
+ return `(${ninClauses.join(" AND ")})`;
71
+ }
72
+ case "$between": {
73
+ if (!Array.isArray(value) || value.length !== 2)
74
+ throw new Error("$between requires [low, high]");
75
+ const [low, high] = value;
76
+ bindValues.push(low, high);
77
+ const posLow = bindValues.length - 1;
78
+ const posHigh = bindValues.length;
79
+ return `JSON_EXISTS(metadata, '$.${column}?(@ >= $low && @ <= $high)' PASSING :${posLow} AS "low", :${posHigh} AS "high")`;
80
+ }
81
+ case "$exists":
82
+ if (value) {
83
+ return `JSON_EXISTS(metadata, '$.${column}')`;
84
+ }
85
+ else {
86
+ return `NOT JSON_EXISTS(metadata, '$.${column}')`;
87
+ }
88
+ default:
89
+ throw new Error(`Unsupported operator: ${operator}`);
90
+ }
91
+ }
92
+ // Helper: build JSON_EXISTS clause for comparison
93
+ function jsonCompare(column, op, value, bindValues) {
94
+ bindValues.push(value);
95
+ const pos = bindValues.length;
96
+ const alias = `val${pos}`; // unique bind alias
97
+ const operator = op === "=" ? "==" : op; // Oracle requires '==' for equality
98
+ return `JSON_EXISTS(metadata, '$.${column}?(@ ${operator} $${alias})' PASSING :${pos} AS "${alias}")`;
99
+ }
100
+ exports.DistanceStrategy = {
101
+ COSINE: "COSINE",
102
+ DOT_PRODUCT: "DOT",
103
+ EUCLIDEAN: "EUCLIDEAN",
104
+ MANHATTAN: "MANHATTAN",
105
+ HAMMING: "HAMMING",
106
+ EUCLIDEAN_SQUARED: "EUCLIDEAN_SQUARED",
107
+ };
108
+ function handleError(error) {
109
+ // Type guard to check if the error is an object and has 'name' and 'message' properties
110
+ if (typeof error === "object" &&
111
+ error !== null &&
112
+ "name" in error &&
113
+ "message" in error) {
114
+ const err = error; // Type assertion based on guarded checks
115
+ // Handle specific error types based on the name property
116
+ switch (err.name) {
117
+ case "RuntimeError":
118
+ throw new Error("Database operation failed due to a runtime error.");
119
+ case "ValidationError":
120
+ throw new Error("Operation failed due to a validation error.");
121
+ default:
122
+ throw new Error(`An unexpected error occurred during the operation. ${error}`);
123
+ }
124
+ }
125
+ throw new Error(`An unknown and unexpected error occurred. ${error}`);
126
+ }
127
+ function isPool(client) {
128
+ return "getConnection" in client;
129
+ }
130
+ function quoteIdentifier(identifier) {
131
+ const name = identifier.trim();
132
+ const validateRegex = /^(?:"[^"]+"|[^".]+)(?:\.(?:"[^"]+"|[^".]+))*$/;
133
+ if (!validateRegex.test(name)) {
134
+ throw new Error(`Identifier name ${identifier} is not valid.`);
135
+ }
136
+ // extracts parts of the identifier with quoted and unquoted.
137
+ const matchRegex = /"([^"]+)"|([^".]+)/g;
138
+ const groups = [];
139
+ for (const match of name.matchAll(matchRegex)) {
140
+ groups.push(match[1] || match[2]);
141
+ }
142
+ const quotedParts = groups.map((g) => `"${g}"`);
143
+ return quotedParts.join(".");
144
+ }
145
+ async function createTable(connection, tableName, embeddingDim) {
146
+ const colsDict = {
147
+ id: "RAW(16) DEFAULT SYS_GUID() PRIMARY KEY",
148
+ embedding: `vector(${embeddingDim}, FLOAT32)`,
149
+ text: "CLOB",
150
+ metadata: "JSON",
151
+ };
152
+ try {
153
+ const ddlBody = Object.entries(colsDict)
154
+ .map(([colName, colType]) => `${colName} ${colType}`)
155
+ .join(", ");
156
+ const ddl = `CREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)}
157
+ (
158
+ ${ddlBody}
159
+ )`;
160
+ await connection.execute(ddl);
161
+ }
162
+ catch (error) {
163
+ handleError(error);
164
+ }
165
+ }
166
+ exports.createTable = createTable;
167
+ function _getIndexName(baseName) {
168
+ const uniqueId = crypto.randomUUID().replace(/-/g, "");
169
+ return `${baseName}_${uniqueId}`;
170
+ }
171
+ async function createIndex(client, vectorStore, params) {
172
+ const idxType = params?.idxType || "HNSW";
173
+ if (idxType === "IVF") {
174
+ await createIVFIndex(client, vectorStore, params);
175
+ }
176
+ else {
177
+ await createHNSWIndex(client, vectorStore, params);
178
+ }
179
+ }
180
+ exports.createIndex = createIndex;
181
+ async function createHNSWIndex(connection, oraclevs, params) {
182
+ try {
183
+ const defaults = {
184
+ idxName: "HNSW",
185
+ idxType: "HNSW",
186
+ neighbors: 32,
187
+ efConstruction: 200,
188
+ accuracy: 90,
189
+ parallel: 8,
190
+ };
191
+ // if params then copy params to config
192
+ const config = params
193
+ ? { ...params }
194
+ : { ...defaults };
195
+ // Ensure compulsory parts are included
196
+ const compulsoryKeys = ["idxName", "parallel"];
197
+ for (const key of compulsoryKeys) {
198
+ if (!(key in config)) {
199
+ if (key === "idxName") {
200
+ config[key] = _getIndexName(defaults[key]);
201
+ }
202
+ else {
203
+ config[key] = defaults[key];
204
+ }
205
+ }
206
+ }
207
+ // Validate keys in config against defaults
208
+ for (const key of Object.keys(config)) {
209
+ if (!(key in defaults)) {
210
+ throw new Error(`Invalid parameter: ${key}`);
211
+ }
212
+ }
213
+ const { idxName } = config;
214
+ const baseSql = `CREATE VECTOR INDEX IF NOT EXISTS ${quoteIdentifier(idxName)}
215
+ ON ${oraclevs.tableName}(embedding)
216
+ ORGANIZATION INMEMORY NEIGHBOR GRAPH`;
217
+ const accuracyPart = config.accuracy
218
+ ? ` WITH TARGET ACCURACY ${config.accuracy}`
219
+ : "";
220
+ const distancePart = ` DISTANCE ${oraclevs.distanceStrategy}`;
221
+ let parametersPart = "";
222
+ if ("neighbors" in config && "efConstruction" in config) {
223
+ parametersPart = ` parameters (type ${config.idxType},
224
+ neighbors ${config.neighbors},
225
+ efConstruction ${config.efConstruction})`;
226
+ }
227
+ else if ("neighbors" in config && !("efConstruction" in config)) {
228
+ config.efConstruction = defaults.efConstruction;
229
+ parametersPart = ` parameters (type ${config.idxType},
230
+ neighbors ${config.neighbors},
231
+ efConstruction ${config.efConstruction})`;
232
+ }
233
+ else if (!("neighbors" in config) && "efConstruction" in config) {
234
+ config.neighbors = defaults.neighbors;
235
+ parametersPart = ` parameters (type ${config.idxType},
236
+ neighbors ${config.neighbors},
237
+ efConstruction ${config.efConstruction})`;
238
+ }
239
+ const parallelPart = ` PARALLEL ${config.parallel}`;
240
+ const ddl = baseSql + accuracyPart + distancePart + parametersPart + parallelPart;
241
+ await connection.execute(ddl);
242
+ }
243
+ catch (error) {
244
+ handleError(error);
245
+ }
246
+ }
247
+ async function createIVFIndex(connection, oraclevs, params) {
248
+ try {
249
+ const defaults = {
250
+ idxName: "IVF",
251
+ idxType: "IVF",
252
+ neighborPart: 32,
253
+ accuracy: 90,
254
+ parallel: 8,
255
+ };
256
+ // Combine defaults with any provided params. Note: params could contain keys not explicitly declared in IndexConfig
257
+ const config = params
258
+ ? { ...params }
259
+ : { ...defaults };
260
+ // Ensure compulsory parts are included
261
+ const compulsoryKeys = ["idxName", "parallel"];
262
+ for (const key of compulsoryKeys) {
263
+ if (!(key in config)) {
264
+ if (key === "idxName") {
265
+ config[key] = _getIndexName(defaults[key]);
266
+ }
267
+ else {
268
+ config[key] = defaults[key];
269
+ }
270
+ }
271
+ }
272
+ // Validate keys in config against defaults
273
+ for (const key of Object.keys(config)) {
274
+ if (!(key in defaults)) {
275
+ throw new Error(`Invalid parameter: ${key}`);
276
+ }
277
+ }
278
+ // Base SQL statement
279
+ const { idxName } = config;
280
+ const baseSql = `CREATE VECTOR INDEX IF NOT EXISTS ${quoteIdentifier(idxName)}
281
+ ON ${oraclevs.tableName}(embedding)
282
+ ORGANIZATION NEIGHBOR PARTITIONS`;
283
+ // Optional parts depending on parameters
284
+ const accuracyPart = config.accuracy
285
+ ? ` WITH TARGET ACCURACY ${config.accuracy}`
286
+ : "";
287
+ const distancePart = ` DISTANCE ${oraclevs.distanceStrategy}`;
288
+ let parametersPart = "";
289
+ if ("idxType" in config && "neighborPart" in config) {
290
+ parametersPart = ` PARAMETERS (type ${config.idxType},
291
+ neighbor partitions ${config.neighborPart})`;
292
+ }
293
+ // Always included part for parallel - assuming parallel is compulsory and always included
294
+ const parallelPart = ` PARALLEL ${config.parallel}`;
295
+ // Combine all parts
296
+ const ddl = baseSql + accuracyPart + distancePart + parametersPart + parallelPart;
297
+ await connection.execute(ddl);
298
+ }
299
+ catch (error) {
300
+ handleError(error);
301
+ }
302
+ }
303
+ async function dropTablePurge(connection, tableName) {
304
+ try {
305
+ const ddl = `DROP TABLE IF EXISTS ${quoteIdentifier(tableName)} PURGE`;
306
+ await connection.execute(ddl);
307
+ }
308
+ catch (error) {
309
+ handleError(error);
310
+ }
311
+ }
312
+ exports.dropTablePurge = dropTablePurge;
313
+ class OracleVS extends vectorstores_1.VectorStore {
314
+ _vectorstoreType() {
315
+ return "oraclevs";
316
+ }
317
+ constructor(embeddings, dbConfig) {
318
+ super(embeddings, dbConfig);
319
+ Object.defineProperty(this, "client", {
320
+ enumerable: true,
321
+ configurable: true,
322
+ writable: true,
323
+ value: void 0
324
+ });
325
+ Object.defineProperty(this, "embeddingDimension", {
326
+ enumerable: true,
327
+ configurable: true,
328
+ writable: true,
329
+ value: void 0
330
+ });
331
+ Object.defineProperty(this, "tableName", {
332
+ enumerable: true,
333
+ configurable: true,
334
+ writable: true,
335
+ value: void 0
336
+ });
337
+ Object.defineProperty(this, "distanceStrategy", {
338
+ enumerable: true,
339
+ configurable: true,
340
+ writable: true,
341
+ value: exports.DistanceStrategy.COSINE
342
+ });
343
+ Object.defineProperty(this, "filter", {
344
+ enumerable: true,
345
+ configurable: true,
346
+ writable: true,
347
+ value: void 0
348
+ });
349
+ Object.defineProperty(this, "query", {
350
+ enumerable: true,
351
+ configurable: true,
352
+ writable: true,
353
+ value: void 0
354
+ });
355
+ try {
356
+ this.client = dbConfig.client;
357
+ this.tableName = quoteIdentifier(dbConfig.tableName);
358
+ this.distanceStrategy =
359
+ dbConfig.distanceStrategy ?? this.distanceStrategy;
360
+ this.query = dbConfig.query;
361
+ this.filter = dbConfig.filter;
362
+ }
363
+ catch (error) {
364
+ handleError(error);
365
+ }
366
+ }
367
+ async getEmbeddingDimension(query) {
368
+ const embeddingVector = await this.embeddings.embedQuery(query);
369
+ return embeddingVector.length;
370
+ }
371
+ async initialize() {
372
+ let connection = null;
373
+ try {
374
+ this.embeddingDimension = await this.getEmbeddingDimension(this.query);
375
+ connection = await this.getConnection();
376
+ await createTable(connection, this.tableName, this.embeddingDimension);
377
+ }
378
+ catch (error) {
379
+ handleError(error);
380
+ }
381
+ finally {
382
+ if (connection)
383
+ await this.retConnection(connection);
384
+ }
385
+ }
386
+ async getConnection() {
387
+ try {
388
+ if (isPool(this.client)) {
389
+ return await this.client.getConnection();
390
+ }
391
+ return this.client;
392
+ }
393
+ catch (error) {
394
+ handleError(error);
395
+ }
396
+ }
397
+ // Close connection or return it to the pool
398
+ async retConnection(connection) {
399
+ try {
400
+ // If the client is a pool, close the connection (return it to the pool)
401
+ if (isPool(this.client)) {
402
+ await connection.close();
403
+ }
404
+ }
405
+ catch (error) {
406
+ console.error("Error in retConnection:", error);
407
+ throw error;
408
+ }
409
+ }
410
+ /**
411
+ * Method to add vectors to the Oracle database.
412
+ * @param vectors The vectors to add.
413
+ * @param documents The documents associated with the vectors.
414
+ * @param options
415
+ * @returns Promise that resolves when the vectors have been added.
416
+ */
417
+ async addVectors(vectors, documents, options) {
418
+ if (vectors.length === 0) {
419
+ throw new Error("Vectors input null. Nothing to add...");
420
+ }
421
+ const ids = options?.ids;
422
+ let connection = null;
423
+ try {
424
+ // Ensure there are IDs for all documents
425
+ if (ids !== undefined && ids.length !== vectors.length) {
426
+ throw new Error("The number of ids must match the number of vectors provided.");
427
+ }
428
+ connection = await this.getConnection();
429
+ const binds = [];
430
+ for (let index = 0; index < documents.length; index += 1) {
431
+ const doc = documents[index];
432
+ let processedId;
433
+ if (ids) {
434
+ processedId = (0, crypto_1.createHash)("sha256")
435
+ .update(ids[index])
436
+ .digest("hex")
437
+ .substring(0, 16)
438
+ .toUpperCase();
439
+ }
440
+ else {
441
+ const sourceId = doc.metadata.id ?? crypto.randomUUID();
442
+ processedId = (0, crypto_1.createHash)("sha256")
443
+ .update(sourceId)
444
+ .digest("hex")
445
+ .substring(0, 16)
446
+ .toUpperCase();
447
+ }
448
+ const idBuffer = Buffer.from(processedId, "hex");
449
+ const bind = {
450
+ id: idBuffer,
451
+ text: doc.pageContent,
452
+ metadata: doc.metadata,
453
+ embedding: new Float32Array(vectors[index]),
454
+ };
455
+ binds.push(bind);
456
+ }
457
+ const sql = `INSERT INTO ${this.tableName} (id, embedding, text, metadata )
458
+ VALUES (:id, :embedding, :text, :metadata)`;
459
+ const options = {
460
+ bindDefs: {
461
+ id: { type: oracledb_1.default.BUFFER, maxSize: 24 },
462
+ text: { type: oracledb_1.default.STRING, maxSize: 10000000 },
463
+ metadata: { type: oracledb_1.default.DB_TYPE_JSON },
464
+ embedding: { type: oracledb_1.default.DB_TYPE_VECTOR },
465
+ },
466
+ autoCommit: false,
467
+ };
468
+ await connection.executeMany(sql, binds, options);
469
+ // Commit once all inserts are queued up
470
+ await connection.commit();
471
+ console.log("All documents have been inserted and committed.");
472
+ }
473
+ catch (error) {
474
+ handleError(error);
475
+ }
476
+ finally {
477
+ if (connection) {
478
+ await this.retConnection(connection);
479
+ }
480
+ }
481
+ return ids;
482
+ }
483
+ async addDocuments(documents, options) {
484
+ const texts = documents.map(({ pageContent }) => pageContent);
485
+ return this.addVectors(await this.embeddings.embedDocuments(texts), documents, options);
486
+ }
487
+ /**
488
+ * Method to search for vectors that are similar to a given query vector.
489
+ * @param query The query vector.
490
+ * @param k The number of similar vectors to return.
491
+ * @param filter Optional filter for the search results.
492
+ * @returns Promise that resolves with an array of tuples, each containing a Document and a score.
493
+ */
494
+ async similaritySearchByVectorReturningEmbeddings(query, k = 4, filter) {
495
+ const docsScoresAndEmbeddings = [];
496
+ let connection = null;
497
+ try {
498
+ const convertedEmbedding = new Float32Array(query);
499
+ const bindValues = [convertedEmbedding];
500
+ let sqlQuery = `
501
+ SELECT id,
502
+ text,
503
+ metadata,
504
+ vector_distance(embedding, :1, ${this.distanceStrategy}) as distance,
505
+ embedding
506
+ FROM ${this.tableName} `;
507
+ if (filter && Object.keys(filter).length > 0) {
508
+ sqlQuery += ` WHERE ${generateWhereClause(filter, bindValues)}`;
509
+ }
510
+ bindValues.push(k);
511
+ sqlQuery += ` ORDER BY distance FETCH APPROX FIRST :${bindValues.length} ROWS ONLY `;
512
+ // Execute the query
513
+ connection = await this.getConnection();
514
+ const resultSet = await connection.execute(sqlQuery, bindValues, {
515
+ fetchInfo: {
516
+ TEXT: { type: oracledb_1.default.STRING },
517
+ },
518
+ });
519
+ if (Array.isArray(resultSet.rows) && resultSet.rows.length > 0) {
520
+ const rows = resultSet.rows;
521
+ for (let idx = 0; idx < resultSet.rows.length; idx += 1) {
522
+ const row = rows[idx];
523
+ const text = row[1];
524
+ const metadata = row[2];
525
+ const distance = row[3];
526
+ const embedding = row[4];
527
+ const document = new documents_1.Document({
528
+ pageContent: text || "",
529
+ metadata: metadata || {},
530
+ id: row[0].toString("hex"),
531
+ });
532
+ docsScoresAndEmbeddings.push([document, distance, embedding]);
533
+ }
534
+ }
535
+ else {
536
+ // Throw an exception if no rows are found
537
+ throw new Error("No rows found.");
538
+ }
539
+ }
540
+ finally {
541
+ if (connection) {
542
+ await connection.close();
543
+ }
544
+ }
545
+ return docsScoresAndEmbeddings;
546
+ }
547
+ async similaritySearchVectorWithScore(query, k, filter) {
548
+ const docsScoresAndEmbeddings = await this.similaritySearchByVectorReturningEmbeddings(query, k, filter);
549
+ return docsScoresAndEmbeddings.map(([document, score]) => [
550
+ document,
551
+ score,
552
+ ]);
553
+ }
554
+ /**
555
+ * Return documents selected using the maximal marginal relevance.
556
+ * Maximal marginal relevance optimizes for similarity to the query AND diversity
557
+ * among selected documents.
558
+ *
559
+ * @param {string} query - Text to look up documents similar to.
560
+ * @param options
561
+ * @param {number} options.k - Number of documents to return.
562
+ * @param {number} options.fetchK - Number of documents to fetch before passing to the MMR algorithm.
563
+ * @param {number} options.lambda - Number between 0 and 1 that determines the degree of diversity among the results,
564
+ * where 0 corresponds to maximum diversity and 1 to minimum diversity.
565
+ * @param {this["FilterType"]} options.filter - Optional filter
566
+ * @param _callbacks
567
+ *
568
+ * @returns {Promise<DocumentInterface[]>} - List of documents selected by maximal marginal relevance.
569
+ */
570
+ async maxMarginalRelevanceSearch(query, options) {
571
+ const embedding = await this.embeddings.embedQuery(query);
572
+ return await this.maxMarginalRelevanceSearchByVector(embedding, options);
573
+ }
574
+ async maxMarginalRelevanceSearchByVector(embedding, options) {
575
+ // Fetch documents and their scores. This calls the previously adapted function.
576
+ const docsAndScores = await this.maxMarginalRelevanceSearchWithScoreByVector(embedding, options);
577
+ // Extract and return only the documents from the results
578
+ return docsAndScores.map((ds) => ds.document);
579
+ }
580
+ async maxMarginalRelevanceSearchWithScoreByVector(embedding, options) {
581
+ // Fetch documents and their scores.
582
+ const docsScoresEmbeddings = await this.similaritySearchByVectorReturningEmbeddings(embedding, options.fetchK, options.filter);
583
+ if (!docsScoresEmbeddings.length) {
584
+ return [];
585
+ }
586
+ // Split documents, scores, and embeddings
587
+ const documents = docsScoresEmbeddings.map(([document]) => document);
588
+ const scores = docsScoresEmbeddings.map(([, score]) => score);
589
+ const embeddings = docsScoresEmbeddings.map(([, , embedding]) => new Float32Array(embedding));
590
+ // Convert all embeddings to Float32Array for consistency
591
+ const consistentEmbeddings = embeddings.map((embedding) => Array.from(embedding));
592
+ const queryEmbedding = Array.from(embedding);
593
+ // Ensure lambdaMult has a default value if not provided
594
+ const lambdaMult = options.lambda ?? 0.5;
595
+ const mmrSelectedIndices = (0, math_1.maximalMarginalRelevance)(queryEmbedding, consistentEmbeddings, lambdaMult, options.k);
596
+ // Filter documents based on MMR-selected indices and map scores
597
+ return mmrSelectedIndices.map((index) => ({
598
+ document: documents[index],
599
+ score: scores[index],
600
+ }));
601
+ }
602
+ async delete(params) {
603
+ let connection = null;
604
+ try {
605
+ connection = await this.getConnection();
606
+ const options = { autoCommit: true };
607
+ if (params.ids && params.ids.length > 0) {
608
+ // Dynamically create placeholders
609
+ const placeholders = params.ids
610
+ .map((_, index) => `:${index + 1}`)
611
+ .join(",");
612
+ // Prepare the query
613
+ const query = `DELETE FROM ${this.tableName} WHERE id IN (${placeholders})`;
614
+ // Execute the query with the IDs as bind parameters
615
+ await connection.execute(query, [...params.ids], options);
616
+ }
617
+ else if (params.deleteAll) {
618
+ await connection.execute(`TRUNCATE TABLE ${this.tableName}`, [], options);
619
+ }
620
+ }
621
+ catch (error) {
622
+ handleError(error);
623
+ }
624
+ finally {
625
+ if (connection)
626
+ await connection.close();
627
+ }
628
+ }
629
+ static async fromDocuments(documents, embeddings, dbConfig) {
630
+ const { client } = dbConfig;
631
+ if (!client)
632
+ throw new Error("client parameter is required...");
633
+ try {
634
+ const vss = new OracleVS(embeddings, dbConfig);
635
+ await vss.initialize();
636
+ const texts = documents.map(({ pageContent }) => pageContent);
637
+ const vectors = await embeddings.embedDocuments(texts);
638
+ // Assuming a method exists to handle adding texts and metadata appropriately
639
+ await vss.addVectors(vectors, documents);
640
+ return vss;
641
+ }
642
+ catch (error) {
643
+ handleError(error);
644
+ }
645
+ }
646
+ /**
647
+ *
648
+ * @returns Promise that resolves when all connections
649
+ * inside the pool are terminated.
650
+ */
651
+ async end() {
652
+ if (isPool(this.client)) {
653
+ await this.client?.close();
654
+ }
655
+ }
656
+ }
657
+ exports.OracleVS = OracleVS;
658
+ //# sourceMappingURL=vectorstores.cjs.map