@mastra/couchbase 0.10.3 → 0.11.0-alpha.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.
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
1
2
  import { MastraVector } from '@mastra/core/vector';
2
3
  import { connect, SearchRequest, VectorSearch, VectorQuery, MutateInSpec } from 'couchbase';
3
4
 
@@ -19,26 +20,45 @@ var CouchbaseVector = class extends MastraVector {
19
20
  vector_dimension;
20
21
  constructor({ connectionString, username, password, bucketName, scopeName, collectionName }) {
21
22
  super();
22
- const baseClusterPromise = connect(connectionString, {
23
- username,
24
- password,
25
- configProfile: "wanDevelopment"
26
- });
27
- const telemetry = this.__getTelemetry();
28
- this.clusterPromise = telemetry?.traceClass(baseClusterPromise, {
29
- spanNamePrefix: "couchbase-vector",
30
- attributes: {
31
- "vector.type": "couchbase"
32
- }
33
- }) ?? baseClusterPromise;
34
- this.cluster = null;
35
- this.bucketName = bucketName;
36
- this.collectionName = collectionName;
37
- this.scopeName = scopeName;
38
- this.collection = null;
39
- this.bucket = null;
40
- this.scope = null;
41
- this.vector_dimension = null;
23
+ try {
24
+ const baseClusterPromise = connect(connectionString, {
25
+ username,
26
+ password,
27
+ configProfile: "wanDevelopment"
28
+ });
29
+ const telemetry = this.__getTelemetry();
30
+ this.clusterPromise = telemetry?.traceClass(baseClusterPromise, {
31
+ spanNamePrefix: "couchbase-vector",
32
+ attributes: {
33
+ "vector.type": "couchbase"
34
+ }
35
+ }) ?? baseClusterPromise;
36
+ this.cluster = null;
37
+ this.bucketName = bucketName;
38
+ this.collectionName = collectionName;
39
+ this.scopeName = scopeName;
40
+ this.collection = null;
41
+ this.bucket = null;
42
+ this.scope = null;
43
+ this.vector_dimension = null;
44
+ } catch (error) {
45
+ throw new MastraError(
46
+ {
47
+ id: "COUCHBASE_VECTOR_INITIALIZE_FAILED",
48
+ domain: ErrorDomain.STORAGE,
49
+ category: ErrorCategory.THIRD_PARTY,
50
+ details: {
51
+ connectionString,
52
+ username,
53
+ password,
54
+ bucketName,
55
+ scopeName,
56
+ collectionName
57
+ }
58
+ },
59
+ error
60
+ );
61
+ }
42
62
  }
43
63
  async getCollection() {
44
64
  if (!this.cluster) {
@@ -52,11 +72,11 @@ var CouchbaseVector = class extends MastraVector {
52
72
  return this.collection;
53
73
  }
54
74
  async createIndex({ indexName, dimension, metric = "dotproduct" }) {
55
- await this.getCollection();
56
- if (!Number.isInteger(dimension) || dimension <= 0) {
57
- throw new Error("Dimension must be a positive integer");
58
- }
59
75
  try {
76
+ await this.getCollection();
77
+ if (!Number.isInteger(dimension) || dimension <= 0) {
78
+ throw new Error("Dimension must be a positive integer");
79
+ }
60
80
  await this.scope.searchIndexes().upsertIndex({
61
81
  name: indexName,
62
82
  sourceName: this.bucketName,
@@ -143,78 +163,129 @@ var CouchbaseVector = class extends MastraVector {
143
163
  await this.validateExistingIndex(indexName, dimension, metric);
144
164
  return;
145
165
  }
146
- throw error;
166
+ throw new MastraError(
167
+ {
168
+ id: "COUCHBASE_VECTOR_CREATE_INDEX_FAILED",
169
+ domain: ErrorDomain.STORAGE,
170
+ category: ErrorCategory.THIRD_PARTY,
171
+ details: {
172
+ indexName,
173
+ dimension,
174
+ metric
175
+ }
176
+ },
177
+ error
178
+ );
147
179
  }
148
180
  }
149
181
  async upsert({ vectors, metadata, ids }) {
150
- await this.getCollection();
151
- if (!vectors || vectors.length === 0) {
152
- throw new Error("No vectors provided");
153
- }
154
- if (this.vector_dimension) {
155
- for (const vector of vectors) {
156
- if (!vector || this.vector_dimension !== vector.length) {
157
- throw new Error("Vector dimension mismatch");
182
+ try {
183
+ await this.getCollection();
184
+ if (!vectors || vectors.length === 0) {
185
+ throw new Error("No vectors provided");
186
+ }
187
+ if (this.vector_dimension) {
188
+ for (const vector of vectors) {
189
+ if (!vector || this.vector_dimension !== vector.length) {
190
+ throw new Error("Vector dimension mismatch");
191
+ }
158
192
  }
159
193
  }
160
- }
161
- const pointIds = ids || vectors.map(() => crypto.randomUUID());
162
- const records = vectors.map((vector, i) => {
163
- const metadataObj = metadata?.[i] || {};
164
- const record = {
165
- embedding: vector,
166
- metadata: metadataObj
167
- };
168
- if (metadataObj.text) {
169
- record.content = metadataObj.text;
194
+ const pointIds = ids || vectors.map(() => crypto.randomUUID());
195
+ const records = vectors.map((vector, i) => {
196
+ const metadataObj = metadata?.[i] || {};
197
+ const record = {
198
+ embedding: vector,
199
+ metadata: metadataObj
200
+ };
201
+ if (metadataObj.text) {
202
+ record.content = metadataObj.text;
203
+ }
204
+ return record;
205
+ });
206
+ const allPromises = [];
207
+ for (let i = 0; i < records.length; i++) {
208
+ allPromises.push(this.collection.upsert(pointIds[i], records[i]));
170
209
  }
171
- return record;
172
- });
173
- const allPromises = [];
174
- for (let i = 0; i < records.length; i++) {
175
- allPromises.push(this.collection.upsert(pointIds[i], records[i]));
210
+ await Promise.all(allPromises);
211
+ return pointIds;
212
+ } catch (error) {
213
+ throw new MastraError(
214
+ {
215
+ id: "COUCHBASE_VECTOR_UPSERT_FAILED",
216
+ domain: ErrorDomain.STORAGE,
217
+ category: ErrorCategory.THIRD_PARTY
218
+ },
219
+ error
220
+ );
176
221
  }
177
- await Promise.all(allPromises);
178
- return pointIds;
179
222
  }
180
223
  async query({ indexName, queryVector, topK = 10, includeVector = false }) {
181
- await this.getCollection();
182
- const index_stats = await this.describeIndex({ indexName });
183
- if (queryVector.length !== index_stats.dimension) {
184
- throw new Error(`Query vector dimension mismatch. Expected ${index_stats.dimension}, got ${queryVector.length}`);
185
- }
186
- let request = SearchRequest.create(
187
- VectorSearch.fromVectorQuery(VectorQuery.create("embedding", queryVector).numCandidates(topK))
188
- );
189
- const results = await this.scope.search(indexName, request, {
190
- fields: ["*"]
191
- });
192
- if (includeVector) {
193
- throw new Error("Including vectors in search results is not yet supported by the Couchbase vector store");
194
- }
195
- const output = [];
196
- for (const match of results.rows) {
197
- const cleanedMetadata = {};
198
- const fields = match.fields || {};
199
- for (const key in fields) {
200
- if (Object.prototype.hasOwnProperty.call(fields, key)) {
201
- const newKey = key.startsWith("metadata.") ? key.substring("metadata.".length) : key;
202
- cleanedMetadata[newKey] = fields[key];
203
- }
224
+ try {
225
+ await this.getCollection();
226
+ const index_stats = await this.describeIndex({ indexName });
227
+ if (queryVector.length !== index_stats.dimension) {
228
+ throw new Error(
229
+ `Query vector dimension mismatch. Expected ${index_stats.dimension}, got ${queryVector.length}`
230
+ );
204
231
  }
205
- output.push({
206
- id: match.id,
207
- score: match.score || 0,
208
- metadata: cleanedMetadata
209
- // Use the cleaned metadata object
232
+ let request = SearchRequest.create(
233
+ VectorSearch.fromVectorQuery(VectorQuery.create("embedding", queryVector).numCandidates(topK))
234
+ );
235
+ const results = await this.scope.search(indexName, request, {
236
+ fields: ["*"]
210
237
  });
238
+ if (includeVector) {
239
+ throw new Error("Including vectors in search results is not yet supported by the Couchbase vector store");
240
+ }
241
+ const output = [];
242
+ for (const match of results.rows) {
243
+ const cleanedMetadata = {};
244
+ const fields = match.fields || {};
245
+ for (const key in fields) {
246
+ if (Object.prototype.hasOwnProperty.call(fields, key)) {
247
+ const newKey = key.startsWith("metadata.") ? key.substring("metadata.".length) : key;
248
+ cleanedMetadata[newKey] = fields[key];
249
+ }
250
+ }
251
+ output.push({
252
+ id: match.id,
253
+ score: match.score || 0,
254
+ metadata: cleanedMetadata
255
+ // Use the cleaned metadata object
256
+ });
257
+ }
258
+ return output;
259
+ } catch (error) {
260
+ throw new MastraError(
261
+ {
262
+ id: "COUCHBASE_VECTOR_QUERY_FAILED",
263
+ domain: ErrorDomain.STORAGE,
264
+ category: ErrorCategory.THIRD_PARTY,
265
+ details: {
266
+ indexName,
267
+ topK
268
+ }
269
+ },
270
+ error
271
+ );
211
272
  }
212
- return output;
213
273
  }
214
274
  async listIndexes() {
215
- await this.getCollection();
216
- const indexes = await this.scope.searchIndexes().getAllIndexes();
217
- return indexes?.map((index) => index.name) || [];
275
+ try {
276
+ await this.getCollection();
277
+ const indexes = await this.scope.searchIndexes().getAllIndexes();
278
+ return indexes?.map((index) => index.name) || [];
279
+ } catch (error) {
280
+ throw new MastraError(
281
+ {
282
+ id: "COUCHBASE_VECTOR_LIST_INDEXES_FAILED",
283
+ domain: ErrorDomain.STORAGE,
284
+ category: ErrorCategory.THIRD_PARTY
285
+ },
286
+ error
287
+ );
288
+ }
218
289
  }
219
290
  /**
220
291
  * Retrieves statistics about a vector index.
@@ -223,29 +294,60 @@ var CouchbaseVector = class extends MastraVector {
223
294
  * @returns A promise that resolves to the index statistics including dimension, count and metric
224
295
  */
225
296
  async describeIndex({ indexName }) {
226
- await this.getCollection();
227
- if (!(await this.listIndexes()).includes(indexName)) {
228
- throw new Error(`Index ${indexName} does not exist`);
297
+ try {
298
+ await this.getCollection();
299
+ if (!(await this.listIndexes()).includes(indexName)) {
300
+ throw new Error(`Index ${indexName} does not exist`);
301
+ }
302
+ const index = await this.scope.searchIndexes().getIndex(indexName);
303
+ const dimensions = index.params.mapping?.types?.[`${this.scopeName}.${this.collectionName}`]?.properties?.embedding?.fields?.[0]?.dims;
304
+ const count = -1;
305
+ const metric = index.params.mapping?.types?.[`${this.scopeName}.${this.collectionName}`]?.properties?.embedding?.fields?.[0]?.similarity;
306
+ return {
307
+ dimension: dimensions,
308
+ count,
309
+ metric: Object.keys(DISTANCE_MAPPING).find(
310
+ (key) => DISTANCE_MAPPING[key] === metric
311
+ )
312
+ };
313
+ } catch (error) {
314
+ throw new MastraError(
315
+ {
316
+ id: "COUCHBASE_VECTOR_DESCRIBE_INDEX_FAILED",
317
+ domain: ErrorDomain.STORAGE,
318
+ category: ErrorCategory.THIRD_PARTY,
319
+ details: {
320
+ indexName
321
+ }
322
+ },
323
+ error
324
+ );
229
325
  }
230
- const index = await this.scope.searchIndexes().getIndex(indexName);
231
- const dimensions = index.params.mapping?.types?.[`${this.scopeName}.${this.collectionName}`]?.properties?.embedding?.fields?.[0]?.dims;
232
- const count = -1;
233
- const metric = index.params.mapping?.types?.[`${this.scopeName}.${this.collectionName}`]?.properties?.embedding?.fields?.[0]?.similarity;
234
- return {
235
- dimension: dimensions,
236
- count,
237
- metric: Object.keys(DISTANCE_MAPPING).find(
238
- (key) => DISTANCE_MAPPING[key] === metric
239
- )
240
- };
241
326
  }
242
327
  async deleteIndex({ indexName }) {
243
- await this.getCollection();
244
- if (!(await this.listIndexes()).includes(indexName)) {
245
- throw new Error(`Index ${indexName} does not exist`);
328
+ try {
329
+ await this.getCollection();
330
+ if (!(await this.listIndexes()).includes(indexName)) {
331
+ throw new Error(`Index ${indexName} does not exist`);
332
+ }
333
+ await this.scope.searchIndexes().dropIndex(indexName);
334
+ this.vector_dimension = null;
335
+ } catch (error) {
336
+ if (error instanceof MastraError) {
337
+ throw error;
338
+ }
339
+ throw new MastraError(
340
+ {
341
+ id: "COUCHBASE_VECTOR_DELETE_INDEX_FAILED",
342
+ domain: ErrorDomain.STORAGE,
343
+ category: ErrorCategory.THIRD_PARTY,
344
+ details: {
345
+ indexName
346
+ }
347
+ },
348
+ error
349
+ );
246
350
  }
247
- await this.scope.searchIndexes().dropIndex(indexName);
248
- this.vector_dimension = null;
249
351
  }
250
352
  /**
251
353
  * Updates a vector by its ID with the provided vector and/or metadata.
@@ -258,25 +360,41 @@ var CouchbaseVector = class extends MastraVector {
258
360
  * @throws Will throw an error if no updates are provided or if the update operation fails.
259
361
  */
260
362
  async updateVector({ id, update }) {
261
- if (!update.vector && !update.metadata) {
262
- throw new Error("No updates provided");
263
- }
264
- if (update.vector && this.vector_dimension && update.vector.length !== this.vector_dimension) {
265
- throw new Error("Vector dimension mismatch");
266
- }
267
- const collection = await this.getCollection();
268
363
  try {
269
- await collection.get(id);
270
- } catch (err) {
271
- if (err.code === 13 || err.message?.includes("document not found")) {
272
- throw new Error(`Vector with id ${id} does not exist`);
364
+ if (!update.vector && !update.metadata) {
365
+ throw new Error("No updates provided");
273
366
  }
274
- throw err;
367
+ if (update.vector && this.vector_dimension && update.vector.length !== this.vector_dimension) {
368
+ throw new Error("Vector dimension mismatch");
369
+ }
370
+ const collection = await this.getCollection();
371
+ try {
372
+ await collection.get(id);
373
+ } catch (err) {
374
+ if (err.code === 13 || err.message?.includes("document not found")) {
375
+ throw new Error(`Vector with id ${id} does not exist`);
376
+ }
377
+ throw err;
378
+ }
379
+ const specs = [];
380
+ if (update.vector) specs.push(MutateInSpec.replace("embedding", update.vector));
381
+ if (update.metadata) specs.push(MutateInSpec.replace("metadata", update.metadata));
382
+ await collection.mutateIn(id, specs);
383
+ } catch (error) {
384
+ throw new MastraError(
385
+ {
386
+ id: "COUCHBASE_VECTOR_UPDATE_FAILED",
387
+ domain: ErrorDomain.STORAGE,
388
+ category: ErrorCategory.THIRD_PARTY,
389
+ details: {
390
+ id,
391
+ hasVectorUpdate: !!update.vector,
392
+ hasMetadataUpdate: !!update.metadata
393
+ }
394
+ },
395
+ error
396
+ );
275
397
  }
276
- const specs = [];
277
- if (update.vector) specs.push(MutateInSpec.replace("embedding", update.vector));
278
- if (update.metadata) specs.push(MutateInSpec.replace("metadata", update.metadata));
279
- await collection.mutateIn(id, specs);
280
398
  }
281
399
  /**
282
400
  * Deletes a vector by its ID.
@@ -286,22 +404,47 @@ var CouchbaseVector = class extends MastraVector {
286
404
  * @throws Will throw an error if the deletion operation fails.
287
405
  */
288
406
  async deleteVector({ id }) {
289
- const collection = await this.getCollection();
290
407
  try {
291
- await collection.get(id);
292
- } catch (err) {
293
- if (err.code === 13 || err.message?.includes("document not found")) {
294
- throw new Error(`Vector with id ${id} does not exist`);
408
+ const collection = await this.getCollection();
409
+ try {
410
+ await collection.get(id);
411
+ } catch (err) {
412
+ if (err.code === 13 || err.message?.includes("document not found")) {
413
+ throw new Error(`Vector with id ${id} does not exist`);
414
+ }
415
+ throw err;
295
416
  }
296
- throw err;
417
+ await collection.remove(id);
418
+ } catch (error) {
419
+ throw new MastraError(
420
+ {
421
+ id: "COUCHBASE_VECTOR_DELETE_FAILED",
422
+ domain: ErrorDomain.STORAGE,
423
+ category: ErrorCategory.THIRD_PARTY,
424
+ details: {
425
+ id
426
+ }
427
+ },
428
+ error
429
+ );
297
430
  }
298
- await collection.remove(id);
299
431
  }
300
432
  async disconnect() {
301
- if (!this.cluster) {
302
- return;
433
+ try {
434
+ if (!this.cluster) {
435
+ return;
436
+ }
437
+ await this.cluster.close();
438
+ } catch (error) {
439
+ throw new MastraError(
440
+ {
441
+ id: "COUCHBASE_VECTOR_DISCONNECT_FAILED",
442
+ domain: ErrorDomain.STORAGE,
443
+ category: ErrorCategory.THIRD_PARTY
444
+ },
445
+ error
446
+ );
303
447
  }
304
- await this.cluster.close();
305
448
  }
306
449
  };
307
450
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/couchbase",
3
- "version": "0.10.3",
3
+ "version": "0.11.0-alpha.1",
4
4
  "description": "Couchbase vector store provider for Mastra",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,16 +26,16 @@
26
26
  "@types/node": "^20.19.0",
27
27
  "@vitest/coverage-v8": "3.2.3",
28
28
  "@vitest/ui": "3.2.3",
29
- "axios": "^1.9.0",
30
- "eslint": "^9.28.0",
29
+ "axios": "^1.10.0",
30
+ "eslint": "^9.29.0",
31
31
  "tsup": "^8.5.0",
32
32
  "typescript": "^5.8.3",
33
33
  "vitest": "^3.2.3",
34
34
  "@internal/lint": "0.0.13",
35
- "@mastra/core": "0.10.6"
35
+ "@mastra/core": "0.10.7-alpha.3"
36
36
  },
37
37
  "peerDependencies": {
38
- "@mastra/core": ">=0.10.4-0 <0.11.0"
38
+ "@mastra/core": ">=0.10.7-0 <0.11.0-0"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",