@oracle/langchain-oracledb 0.2.0 → 1.1.0

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 (42) hide show
  1. package/README.md +62 -1
  2. package/dist/cache.cjs +535 -0
  3. package/dist/cache.cjs.map +1 -0
  4. package/dist/cache.d.cts +168 -0
  5. package/dist/cache.d.ts +168 -0
  6. package/dist/cache.js +527 -0
  7. package/dist/cache.js.map +1 -0
  8. package/dist/chatMessageHistories.cjs +345 -0
  9. package/dist/chatMessageHistories.cjs.map +1 -0
  10. package/dist/chatMessageHistories.d.cts +107 -0
  11. package/dist/chatMessageHistories.d.ts +107 -0
  12. package/dist/chatMessageHistories.js +338 -0
  13. package/dist/chatMessageHistories.js.map +1 -0
  14. package/dist/document_loaders.cjs +4 -3
  15. package/dist/document_loaders.cjs.map +1 -1
  16. package/dist/document_loaders.js +4 -3
  17. package/dist/document_loaders.js.map +1 -1
  18. package/dist/errors.cjs +123 -0
  19. package/dist/errors.cjs.map +1 -0
  20. package/dist/errors.d.cts +57 -0
  21. package/dist/errors.d.ts +57 -0
  22. package/dist/errors.js +113 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/index.cjs +11 -0
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +3 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.js +3 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/utils.cjs +49 -0
  31. package/dist/utils.cjs.map +1 -0
  32. package/dist/utils.d.cts +5 -0
  33. package/dist/utils.d.ts +5 -0
  34. package/dist/utils.js +44 -0
  35. package/dist/utils.js.map +1 -0
  36. package/dist/vectorstores.cjs +156 -108
  37. package/dist/vectorstores.cjs.map +1 -1
  38. package/dist/vectorstores.d.cts +11 -3
  39. package/dist/vectorstores.d.ts +11 -3
  40. package/dist/vectorstores.js +137 -91
  41. package/dist/vectorstores.js.map +1 -1
  42. package/package.json +27 -26
@@ -3,12 +3,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.OracleVS = exports.dropTablePurge = exports.createIndex = exports.createTable = exports.DistanceStrategy = exports.VectorElementFormat = exports.VectorType = exports.generateWhereClause = void 0;
6
+ exports.OracleVS = exports.dropTablePurge = exports.createIndex = exports.createTable = exports.DistanceStrategy = exports.VectorElementFormat = exports.VectorType = exports.generateWhereClause = exports.isLangChainOracleError = exports.OracleVSErrorCode = exports.OracleVSError = exports.ErrorCode = exports.LangChainOracleError = void 0;
7
7
  const node_crypto_1 = __importDefault(require("node:crypto"));
8
8
  const oracledb_1 = __importDefault(require("oracledb"));
9
9
  const vectorstores_1 = require("@langchain/core/vectorstores");
10
10
  const documents_1 = require("@langchain/core/documents");
11
11
  const math_1 = require("@langchain/core/utils/math");
12
+ const errors_js_1 = require("./errors.cjs");
13
+ Object.defineProperty(exports, "LangChainOracleError", { enumerable: true, get: function () { return errors_js_1.LangChainOracleError; } });
14
+ Object.defineProperty(exports, "OracleVSError", { enumerable: true, get: function () { return errors_js_1.LangChainOracleError; } });
15
+ Object.defineProperty(exports, "ErrorCode", { enumerable: true, get: function () { return errors_js_1.ErrorCode; } });
16
+ Object.defineProperty(exports, "OracleVSErrorCode", { enumerable: true, get: function () { return errors_js_1.ErrorCode; } });
17
+ Object.defineProperty(exports, "isLangChainOracleError", { enumerable: true, get: function () { return errors_js_1.isLangChainOracleError; } });
18
+ const utils_js_1 = require("./utils.cjs");
19
+ function validateMetadataKey(column) {
20
+ const pattern = /^[a-zA-Z0-9_.[\],\s*]*$/;
21
+ if (!column || !pattern.test(column)) {
22
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.FILTER_INVALID_METADATA_KEY, column);
23
+ }
24
+ }
12
25
  function generateWhereClause(dbFilter, bindValues) {
13
26
  // Handle $and
14
27
  if ("$and" in dbFilter && Array.isArray(dbFilter.$and)) {
@@ -44,6 +57,7 @@ function generateWhereClause(dbFilter, bindValues) {
44
57
  }
45
58
  exports.generateWhereClause = generateWhereClause;
46
59
  function generateOperatorCondition(column, operator, value, bindValues) {
60
+ validateMetadataKey(column);
47
61
  switch (operator) {
48
62
  case "$eq":
49
63
  return jsonCompare(column, "=", value, bindValues);
@@ -58,20 +72,23 @@ function generateOperatorCondition(column, operator, value, bindValues) {
58
72
  case "$gte":
59
73
  return jsonCompare(column, ">=", value, bindValues);
60
74
  case "$in": {
61
- if (!Array.isArray(value))
62
- throw new Error("$in requires array");
75
+ if (!Array.isArray(value)) {
76
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.FILTER_INVALID_VALUE, "$in requires array");
77
+ }
63
78
  const inClauses = value.map((v) => jsonCompare(column, "=", v, bindValues));
64
79
  return `(${inClauses.join(" OR ")})`;
65
80
  }
66
81
  case "$nin": {
67
- if (!Array.isArray(value))
68
- throw new Error("$nin requires array");
82
+ if (!Array.isArray(value)) {
83
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.FILTER_INVALID_VALUE, "$nin requires array");
84
+ }
69
85
  const ninClauses = value.map((v) => jsonCompare(column, "!=", v, bindValues));
70
86
  return `(${ninClauses.join(" AND ")})`;
71
87
  }
72
88
  case "$between": {
73
- if (!Array.isArray(value) || value.length !== 2)
74
- throw new Error("$between requires [low, high]");
89
+ if (!Array.isArray(value) || value.length !== 2) {
90
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.FILTER_INVALID_VALUE, "$between requires [low, high]");
91
+ }
75
92
  const [low, high] = value;
76
93
  bindValues.push(low, high);
77
94
  const posLow = bindValues.length - 1;
@@ -86,7 +103,7 @@ function generateOperatorCondition(column, operator, value, bindValues) {
86
103
  return `NOT JSON_EXISTS(metadata, '$.${column}')`;
87
104
  }
88
105
  default:
89
- throw new Error(`Unsupported operator: ${operator}`);
106
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.FILTER_UNSUPPORTED_OPERATOR, operator);
90
107
  }
91
108
  }
92
109
  // Helper: build JSON_EXISTS clause for comparison
@@ -116,42 +133,11 @@ exports.DistanceStrategy = {
116
133
  HAMMING: "HAMMING",
117
134
  EUCLIDEAN_SQUARED: "EUCLIDEAN_SQUARED",
118
135
  };
119
- function handleError(error) {
120
- // Type guard to check if the error is an object and has 'name' and 'message' properties
121
- if (typeof error === "object" &&
122
- error !== null &&
123
- "name" in error &&
124
- "message" in error) {
125
- const err = error; // Type assertion based on guarded checks
126
- // Handle specific error types based on the name property
127
- switch (err.name) {
128
- case "RuntimeError":
129
- throw new Error("Database operation failed due to a runtime error.");
130
- case "ValidationError":
131
- throw new Error("Operation failed due to a validation error.");
132
- default:
133
- throw new Error(`An unexpected error occurred during the operation. ${error}`);
134
- }
135
- }
136
- throw new Error(`An unknown and unexpected error occurred. ${error}`);
137
- }
138
136
  function isPool(client) {
139
137
  return "getConnection" in client;
140
138
  }
141
- function quoteIdentifier(identifier) {
142
- const name = identifier.trim();
143
- const validateRegex = /^(?:"[^"]+"|[^".]+)(?:\.(?:"[^"]+"|[^".]+))*$/;
144
- if (!validateRegex.test(name)) {
145
- throw new Error(`Identifier name ${identifier} is not valid.`);
146
- }
147
- // extracts parts of the identifier with quoted and unquoted.
148
- const matchRegex = /"([^"]+)"|([^".]+)/g;
149
- const groups = [];
150
- for (const match of name.matchAll(matchRegex)) {
151
- groups.push(match[1] || match[2]);
152
- }
153
- const quotedParts = groups.map((g) => `"${g}"`);
154
- return quotedParts.join(".");
139
+ function isClientProvider(client) {
140
+ return typeof client === "function";
155
141
  }
156
142
  const isFloat32Array = (value) =>
157
143
  // eslint-disable-next-line no-instanceof/no-instanceof
@@ -186,25 +172,26 @@ function normalizeVectorFormat(value, vectorType) {
186
172
  const format = validFormats.includes(normalizedValue)
187
173
  ? normalizedValue
188
174
  : exports.VectorElementFormat.FLOAT32;
189
- if (vectorType === exports.VectorType.SPARSE && format === exports.VectorElementFormat.BINARY) {
190
- throw new Error("BINARY format is not supported for SPARSE vectors.");
175
+ if (vectorType === exports.VectorType.SPARSE &&
176
+ format === exports.VectorElementFormat.BINARY) {
177
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_CONFIGURATION, "BINARY format is not supported for SPARSE vectors.");
191
178
  }
192
179
  return format;
193
180
  }
194
181
  function buildVectorColumnDefinition(embeddingDim, customization) {
195
182
  if (embeddingDim === undefined || embeddingDim === null) {
196
- throw new Error("Embedding dimension is required to create the vector column.");
183
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_CONFIGURATION, "Embedding dimension is required to create the vector column.");
197
184
  }
198
185
  if (!Number.isInteger(embeddingDim) || embeddingDim <= 0) {
199
- throw new Error("Embedding dimension must be a positive integer.");
186
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_CONFIGURATION, "Embedding dimension must be a positive integer.");
200
187
  }
201
188
  const vectorType = normalizeVectorTypeValue(customization?.vectorType);
202
189
  if (vectorType === exports.VectorType.SPARSE && customization?.format === undefined) {
203
- throw new Error("Sparse vector type requires a vector format to be specified.");
190
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_CONFIGURATION, "Sparse vector type requires a vector format to be specified.");
204
191
  }
205
192
  const format = normalizeVectorFormat(customization?.format, vectorType);
206
193
  if (format === exports.VectorElementFormat.BINARY && embeddingDim % 8 !== 0) {
207
- throw new Error("BINARY vector format requires dimensions to be a multiple of 8.");
194
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_CONFIGURATION, "BINARY vector format requires dimensions to be a multiple of 8.");
208
195
  }
209
196
  const dimensionSegment = String(embeddingDim);
210
197
  const formatSegment = format ?? exports.VectorElementFormat.FLOAT32;
@@ -220,7 +207,7 @@ function escapePlSqlLiteral(sql) {
220
207
  return sql.replace(/'/g, "''");
221
208
  }
222
209
  async function createTable(connection, tableName, embeddingDim, customization) {
223
- const tableIdentifier = quoteIdentifier(tableName);
210
+ const tableIdentifier = (0, utils_js_1.quoteIdentifier)(tableName);
224
211
  const colsDict = {
225
212
  id: "RAW(16) DEFAULT SYS_GUID() PRIMARY KEY",
226
213
  external_id: `VARCHAR2(36) UNIQUE`,
@@ -252,7 +239,7 @@ async function createTable(connection, tableName, embeddingDim, customization) {
252
239
  const columnKey = columnName.trim().toLowerCase();
253
240
  if (!(columnKey in colsDict))
254
241
  continue;
255
- const normalizedColumnIdentifier = `${tableIdentifier}.${quoteIdentifier(columnKey.toUpperCase())}`;
242
+ const normalizedColumnIdentifier = `${tableIdentifier}.${(0, utils_js_1.quoteIdentifier)(columnKey.toUpperCase())}`;
256
243
  const escapedNote = escapeCommentText(trimmedNote);
257
244
  statements.push(`EXECUTE IMMEDIATE 'COMMENT ON COLUMN ${normalizedColumnIdentifier} IS ''${escapedNote}''';`);
258
245
  }
@@ -261,7 +248,7 @@ async function createTable(connection, tableName, embeddingDim, customization) {
261
248
  await connection.execute(block);
262
249
  }
263
250
  catch (error) {
264
- handleError(error);
251
+ (0, errors_js_1.handleError)(error);
265
252
  }
266
253
  }
267
254
  exports.createTable = createTable;
@@ -296,7 +283,7 @@ function convertDenseVectorForFormat(values, format) {
296
283
  for (let i = 0; i < values.length; i += 1) {
297
284
  const rounded = Math.round(values[i]);
298
285
  if (rounded < -128 || rounded > 127) {
299
- throw new Error("INT8 vector values must be within [-128, 127].");
286
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_VALUE, "INT8 vector values must be within [-128, 127].");
300
287
  }
301
288
  clamped[i] = rounded;
302
289
  }
@@ -336,10 +323,10 @@ async function createHNSWIndex(connection, oraclevs, params = {}) {
336
323
  const { idxName = _getIndexName("HNSW"), idxType = "HNSW", neighbors = 32, efConstruction = 200, accuracy = 90, parallel = 8, ...extra } = params;
337
324
  const invalidKeys = Object.keys(extra);
338
325
  if (invalidKeys.length > 0) {
339
- throw new Error(`Invalid parameter(s): ${invalidKeys.join(", ")}`);
326
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_INDEX_PARAMETERS, invalidKeys);
340
327
  }
341
328
  const ddl = `
342
- CREATE VECTOR INDEX IF NOT EXISTS ${quoteIdentifier(idxName)}
329
+ CREATE VECTOR INDEX IF NOT EXISTS ${(0, utils_js_1.quoteIdentifier)(idxName)}
343
330
  ON ${oraclevs.tableName}(embedding)
344
331
  ORGANIZATION INMEMORY NEIGHBOR GRAPH
345
332
  WITH TARGET ACCURACY ${accuracy}
@@ -352,7 +339,7 @@ async function createHNSWIndex(connection, oraclevs, params = {}) {
352
339
  await connection.execute(ddl);
353
340
  }
354
341
  catch (error) {
355
- handleError(error);
342
+ (0, errors_js_1.handleError)(error);
356
343
  }
357
344
  }
358
345
  async function createIVFIndex(connection, oraclevs, params = {}) {
@@ -360,10 +347,10 @@ async function createIVFIndex(connection, oraclevs, params = {}) {
360
347
  const { idxName = _getIndexName("IVF"), idxType = "IVF", neighborPart = 32, accuracy = 90, parallel = 8, ...extra } = params;
361
348
  const invalidKeys = Object.keys(extra);
362
349
  if (invalidKeys.length > 0) {
363
- throw new Error(`Invalid parameter(s): ${invalidKeys.join(", ")}`);
350
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_INDEX_PARAMETERS, invalidKeys);
364
351
  }
365
352
  const ddl = `
366
- CREATE VECTOR INDEX IF NOT EXISTS ${quoteIdentifier(idxName)}
353
+ CREATE VECTOR INDEX IF NOT EXISTS ${(0, utils_js_1.quoteIdentifier)(idxName)}
367
354
  ON ${oraclevs.tableName}(embedding)
368
355
  ORGANIZATION NEIGHBOR PARTITIONS
369
356
  WITH TARGET ACCURACY ${accuracy}
@@ -376,16 +363,16 @@ async function createIVFIndex(connection, oraclevs, params = {}) {
376
363
  await connection.execute(ddl);
377
364
  }
378
365
  catch (error) {
379
- handleError(error);
366
+ (0, errors_js_1.handleError)(error);
380
367
  }
381
368
  }
382
369
  async function dropTablePurge(connection, tableName) {
383
370
  try {
384
- const ddl = `DROP TABLE IF EXISTS ${quoteIdentifier(tableName)} PURGE`;
371
+ const ddl = `DROP TABLE IF EXISTS ${(0, utils_js_1.quoteIdentifier)(tableName)} PURGE`;
385
372
  await connection.execute(ddl);
386
373
  }
387
374
  catch (error) {
388
- handleError(error);
375
+ (0, errors_js_1.handleError)(error);
389
376
  }
390
377
  }
391
378
  exports.dropTablePurge = dropTablePurge;
@@ -401,6 +388,14 @@ class OracleVS extends vectorstores_1.VectorStore {
401
388
  writable: true,
402
389
  value: void 0
403
390
  });
391
+ // Tracks connections borrowed from a pool so retConnection() can return them
392
+ // without closing direct connections owned by the caller.
393
+ Object.defineProperty(this, "poolConnections", {
394
+ enumerable: true,
395
+ configurable: true,
396
+ writable: true,
397
+ value: new WeakSet()
398
+ });
404
399
  Object.defineProperty(this, "embeddingDimension", {
405
400
  enumerable: true,
406
401
  configurable: true,
@@ -457,7 +452,7 @@ class OracleVS extends vectorstores_1.VectorStore {
457
452
  });
458
453
  try {
459
454
  this.client = dbConfig.client;
460
- this.tableName = quoteIdentifier(dbConfig.tableName);
455
+ this.tableName = (0, utils_js_1.quoteIdentifier)(dbConfig.tableName);
461
456
  this.distanceStrategy =
462
457
  dbConfig.distanceStrategy ?? this.distanceStrategy;
463
458
  this.query = dbConfig.query;
@@ -470,20 +465,24 @@ class OracleVS extends vectorstores_1.VectorStore {
470
465
  this.vectorFormat = dbConfig.format;
471
466
  }
472
467
  catch (error) {
473
- handleError(error);
468
+ (0, errors_js_1.handleError)(error);
474
469
  }
475
470
  }
476
471
  getActiveVectorType() {
477
- return this.vectorType ? normalizeVectorTypeValue(this.vectorType) : exports.VectorType.DENSE;
472
+ return this.vectorType
473
+ ? normalizeVectorTypeValue(this.vectorType)
474
+ : exports.VectorType.DENSE;
478
475
  }
479
476
  ensureEmbeddingDimension() {
480
- if (this.embeddingDimension === undefined || this.embeddingDimension === null) {
481
- throw new Error("Embedding dimension is not initialized for this vector store.");
477
+ if (this.embeddingDimension === undefined ||
478
+ this.embeddingDimension === null) {
479
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.STATE_INVALID, "Embedding dimension is not initialized for this vector store.");
482
480
  }
483
481
  return this.embeddingDimension;
484
482
  }
485
483
  prepareVectorForStorage(vector) {
486
- if (this.embeddingDimension === undefined || this.embeddingDimension === null) {
484
+ if (this.embeddingDimension === undefined ||
485
+ this.embeddingDimension === null) {
487
486
  this.embeddingDimension = vector.length;
488
487
  }
489
488
  const dimension = this.ensureEmbeddingDimension();
@@ -493,30 +492,63 @@ class OracleVS extends vectorstores_1.VectorStore {
493
492
  : exports.VectorElementFormat.FLOAT32;
494
493
  if (vectorType === exports.VectorType.SPARSE) {
495
494
  if (vector.length !== dimension) {
496
- throw new Error("Sparse vectors must supply full-dimension arrays for conversion.");
495
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_VALUE, "Sparse vectors must supply full-dimension arrays for conversion.");
497
496
  }
498
- let sparseInput = vector;
497
+ // The dense-input SparseVector constructor stores its values as a
498
+ // Float64Array regardless of the typed array passed in, so FLOAT32 and
499
+ // INT8 query vectors reach the database as FLOAT64 and VECTOR_DISTANCE()
500
+ // fails with ORA-51812 against a non-FLOAT64 sparse column. The
501
+ // object-form constructor preserves the values' typed array, so build
502
+ // the nonzero indices/values explicitly and use that form.
503
+ const indices = [];
504
+ const nonzero = [];
499
505
  if (format === exports.VectorElementFormat.INT8) {
500
- const clamped = new Int8Array(vector.length);
501
506
  for (let i = 0; i < vector.length; i += 1) {
502
- const rounded = Math.round(vector[i]);
503
- if (rounded < -128 || rounded > 127) {
504
- throw new Error("INT8 sparse vector values must be within [-128, 127].");
507
+ const value = vector[i];
508
+ const rounded = Math.round(value);
509
+ if (!Number.isFinite(value) || rounded < -128 || rounded > 127) {
510
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_VALUE, "INT8 sparse vector values must be finite and within [-128, 127].");
511
+ }
512
+ if (rounded !== 0) {
513
+ indices.push(i);
514
+ nonzero.push(rounded);
515
+ }
516
+ }
517
+ }
518
+ else if (format === exports.VectorElementFormat.FLOAT32) {
519
+ for (let i = 0; i < vector.length; i += 1) {
520
+ if (Math.fround(vector[i]) !== 0) {
521
+ indices.push(i);
522
+ nonzero.push(vector[i]);
505
523
  }
506
- clamped[i] = rounded;
507
524
  }
508
- sparseInput = clamped;
525
+ }
526
+ else {
527
+ for (let i = 0; i < vector.length; i += 1) {
528
+ if (vector[i] !== 0) {
529
+ indices.push(i);
530
+ nonzero.push(vector[i]);
531
+ }
532
+ }
533
+ }
534
+ let values;
535
+ if (format === exports.VectorElementFormat.INT8) {
536
+ values = new Int8Array(nonzero);
509
537
  }
510
538
  else if (format === exports.VectorElementFormat.FLOAT64) {
511
- sparseInput = new Float64Array(vector);
539
+ values = new Float64Array(nonzero);
512
540
  }
513
- else if (format === exports.VectorElementFormat.FLOAT32) {
514
- sparseInput = new Float32Array(vector);
541
+ else {
542
+ values = new Float32Array(nonzero);
515
543
  }
516
- return new oracledb_1.default.SparseVector(sparseInput);
544
+ return new oracledb_1.default.SparseVector({
545
+ values,
546
+ indices: new Uint32Array(indices),
547
+ numDimensions: vector.length,
548
+ });
517
549
  }
518
550
  if (vector.length !== dimension) {
519
- throw new Error("Vector length does not match the embedding dimension.");
551
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_INVALID_VALUE, "Vector length does not match the embedding dimension.");
520
552
  }
521
553
  return convertDenseVectorForFormat(vector, format);
522
554
  }
@@ -529,7 +561,7 @@ class OracleVS extends vectorstores_1.VectorStore {
529
561
  if (isSparseVector(value)) {
530
562
  const dense = value.dense?.();
531
563
  if (!dense) {
532
- throw new Error("Unable to expand sparse vector to dense representation.");
564
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_UNSUPPORTED_REPRESENTATION, "Unable to expand sparse vector to dense representation.");
533
565
  }
534
566
  if (isFloat32Array(dense)) {
535
567
  return dense;
@@ -545,13 +577,16 @@ class OracleVS extends vectorstores_1.VectorStore {
545
577
  }
546
578
  return Float32Array.from(dense);
547
579
  }
548
- if (isFloat32Array(value) || isFloat64Array(value) || isInt8Array(value) || isUint8Array(value)) {
580
+ if (isFloat32Array(value) ||
581
+ isFloat64Array(value) ||
582
+ isInt8Array(value) ||
583
+ isUint8Array(value)) {
549
584
  return value;
550
585
  }
551
586
  if (Array.isArray(value)) {
552
587
  return value;
553
588
  }
554
- throw new Error("Received unsupported vector representation from the database.");
589
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VECTOR_UNSUPPORTED_REPRESENTATION, "Received unsupported vector representation from the database.");
555
590
  }
556
591
  // Normalizes any supported vector representation into Float32Array so downstream math
557
592
  // (MMR, distance calculations) can assume a consistent element type without forcing
@@ -589,34 +624,41 @@ class OracleVS extends vectorstores_1.VectorStore {
589
624
  description: this.description,
590
625
  annotations: this.annotations,
591
626
  vectorType: this.vectorType,
592
- format: this.vectorFormat
627
+ format: this.vectorFormat,
593
628
  });
594
629
  }
595
630
  catch (error) {
596
- handleError(error);
631
+ (0, errors_js_1.handleError)(error);
597
632
  }
598
633
  finally {
599
634
  if (connection)
600
635
  await this.retConnection(connection);
601
636
  }
602
637
  }
638
+ async resolveClient() {
639
+ return isClientProvider(this.client) ? await this.client() : this.client;
640
+ }
603
641
  async getConnection() {
604
642
  try {
605
- if (isPool(this.client)) {
606
- return await this.client.getConnection();
643
+ const client = await this.resolveClient();
644
+ if (isPool(client)) {
645
+ const connection = await client.getConnection();
646
+ this.poolConnections.add(connection);
647
+ return connection;
607
648
  }
608
- return this.client;
649
+ return client;
609
650
  }
610
651
  catch (error) {
611
- handleError(error);
652
+ (0, errors_js_1.handleError)(error);
612
653
  }
613
654
  }
614
655
  // Close connection or return it to the pool
615
656
  async retConnection(connection) {
616
657
  try {
617
- // If the client is a pool, close the connection (return it to the pool)
618
- if (isPool(this.client)) {
658
+ // If this public connection came from any pool, close it to return it to the pool.
659
+ if (this.poolConnections.has(connection)) {
619
660
  await connection.close();
661
+ this.poolConnections.delete(connection);
620
662
  }
621
663
  }
622
664
  catch (error) {
@@ -634,16 +676,15 @@ class OracleVS extends vectorstores_1.VectorStore {
634
676
  */
635
677
  async addVectors(vectors, documents, options) {
636
678
  if (vectors.length === 0) {
637
- throw new Error("Vectors input null. Nothing to add...");
679
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VALIDATION_INVALID_INPUT, "Vectors input null. Nothing to add...");
638
680
  }
639
681
  const inputIds = options?.ids;
640
682
  let connection = null;
641
683
  try {
642
684
  // Ensure there are IDs for all documents
643
685
  if (inputIds !== undefined && inputIds.length !== vectors.length) {
644
- throw new Error("The number of ids must match the number of vectors provided.");
686
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VALIDATION_INVALID_INPUT, "The number of ids must match the number of vectors provided.");
645
687
  }
646
- connection = await this.getConnection();
647
688
  const finalIds = [];
648
689
  const binds = [];
649
690
  for (let index = 0; index < documents.length; index += 1) {
@@ -689,13 +730,14 @@ class OracleVS extends vectorstores_1.VectorStore {
689
730
  },
690
731
  autoCommit: false,
691
732
  };
733
+ connection = await this.getConnection();
692
734
  await connection.executeMany(sql, binds, executeOptions);
693
735
  // Commit once all inserts are queued up
694
736
  await connection.commit();
695
737
  return finalIds;
696
738
  }
697
739
  catch (error) {
698
- return handleError(error);
740
+ return (0, errors_js_1.handleError)(error);
699
741
  }
700
742
  finally {
701
743
  if (connection) {
@@ -719,14 +761,22 @@ class OracleVS extends vectorstores_1.VectorStore {
719
761
  let connection = null;
720
762
  try {
721
763
  const bindValues = [this.prepareQueryVector(query)];
764
+ const hasFilter = !!filter && Object.keys(filter).length > 0;
765
+ // Keep the vector index hint for unfiltered searches. When a JSON filter
766
+ // is present, forcing VECTOR_INDEX_TRANSFORM can change result semantics
767
+ // by doing approximate top-k before the filter is applied.
768
+ const selectClause = hasFilter
769
+ ? "SELECT"
770
+ : `SELECT /*+ VECTOR_INDEX_TRANSFORM(${this.tableName}) */`;
722
771
  let sqlQuery = `
723
- SELECT external_id,
772
+ ${selectClause}
773
+ external_id,
724
774
  text,
725
775
  metadata,
726
776
  vector_distance(embedding, :1, ${this.distanceStrategy}) as distance,
727
777
  embedding
728
778
  FROM ${this.tableName} `;
729
- if (filter && Object.keys(filter).length > 0) {
779
+ if (hasFilter) {
730
780
  sqlQuery += ` WHERE ${generateWhereClause(filter, bindValues)}`;
731
781
  }
732
782
  bindValues.push(k);
@@ -754,14 +804,10 @@ class OracleVS extends vectorstores_1.VectorStore {
754
804
  docsScoresAndEmbeddings.push([document, distance, embedding]);
755
805
  }
756
806
  }
757
- else {
758
- // Throw an exception if no rows are found
759
- throw new Error("No rows found.");
760
- }
761
807
  }
762
808
  finally {
763
809
  if (connection) {
764
- await connection.close();
810
+ await this.retConnection(connection);
765
811
  }
766
812
  }
767
813
  return docsScoresAndEmbeddings;
@@ -839,17 +885,19 @@ class OracleVS extends vectorstores_1.VectorStore {
839
885
  }
840
886
  }
841
887
  catch (error) {
842
- handleError(error);
888
+ (0, errors_js_1.handleError)(error);
843
889
  }
844
890
  finally {
845
- if (connection)
846
- await connection.close();
891
+ if (connection) {
892
+ await this.retConnection(connection);
893
+ }
847
894
  }
848
895
  }
849
896
  static async fromDocuments(documents, embeddings, dbConfig, options) {
850
897
  const { client } = dbConfig;
851
- if (!client)
852
- throw new Error("client parameter is required...");
898
+ if (!client) {
899
+ (0, errors_js_1.throwError)(errors_js_1.ErrorCode.VALIDATION_MISSING_REQUIRED_PARAMETER, "client");
900
+ }
853
901
  try {
854
902
  const vss = new OracleVS(embeddings, dbConfig);
855
903
  await vss.initialize();
@@ -860,7 +908,7 @@ class OracleVS extends vectorstores_1.VectorStore {
860
908
  return vss;
861
909
  }
862
910
  catch (error) {
863
- handleError(error);
911
+ (0, errors_js_1.handleError)(error);
864
912
  }
865
913
  }
866
914
  /**
@@ -869,8 +917,8 @@ class OracleVS extends vectorstores_1.VectorStore {
869
917
  * inside the pool are terminated.
870
918
  */
871
919
  async end() {
872
- if (isPool(this.client)) {
873
- await this.client?.close();
920
+ if (!isClientProvider(this.client) && isPool(this.client)) {
921
+ await this.client.close();
874
922
  }
875
923
  }
876
924
  }