@mastra/couchbase 0.0.0-trigger-playground-ui-package-20250506151043 → 0.0.0-update-stores-peerDeps-20250723031338

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,5 +1,6 @@
1
+ import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
1
2
  import { MastraVector } from '@mastra/core/vector';
2
- import { connect, SearchRequest, VectorSearch, VectorQuery } from 'couchbase';
3
+ import { connect, SearchRequest, VectorSearch, VectorQuery, MutateInSpec } from 'couchbase';
3
4
 
4
5
  // src/vector/index.ts
5
6
  var DISTANCE_MAPPING = {
@@ -17,28 +18,47 @@ var CouchbaseVector = class extends MastraVector {
17
18
  bucket;
18
19
  scope;
19
20
  vector_dimension;
20
- constructor(cnn_string, username, password, bucketName, scopeName, collectionName) {
21
+ constructor({ connectionString, username, password, bucketName, scopeName, collectionName }) {
21
22
  super();
22
- const baseClusterPromise = connect(cnn_string, {
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) {
@@ -51,189 +71,380 @@ var CouchbaseVector = class extends MastraVector {
51
71
  }
52
72
  return this.collection;
53
73
  }
54
- async createIndex(params) {
55
- const { indexName, dimension, metric = "dotproduct" } = params;
56
- await this.getCollection();
57
- if (!Number.isInteger(dimension) || dimension <= 0) {
58
- throw new Error("Dimension must be a positive integer");
59
- }
60
- await this.scope.searchIndexes().upsertIndex({
61
- name: indexName,
62
- sourceName: this.bucketName,
63
- type: "fulltext-index",
64
- params: {
65
- doc_config: {
66
- docid_prefix_delim: "",
67
- docid_regexp: "",
68
- mode: "scope.collection.type_field",
69
- type_field: "type"
70
- },
71
- mapping: {
72
- default_analyzer: "standard",
73
- default_datetime_parser: "dateTimeOptional",
74
- default_field: "_all",
75
- default_mapping: {
76
- dynamic: true,
77
- enabled: false
74
+ async createIndex({ indexName, dimension, metric = "dotproduct" }) {
75
+ try {
76
+ await this.getCollection();
77
+ if (!Number.isInteger(dimension) || dimension <= 0) {
78
+ throw new Error("Dimension must be a positive integer");
79
+ }
80
+ await this.scope.searchIndexes().upsertIndex({
81
+ name: indexName,
82
+ sourceName: this.bucketName,
83
+ type: "fulltext-index",
84
+ params: {
85
+ doc_config: {
86
+ docid_prefix_delim: "",
87
+ docid_regexp: "",
88
+ mode: "scope.collection.type_field",
89
+ type_field: "type"
78
90
  },
79
- default_type: "_default",
80
- docvalues_dynamic: true,
81
- // [Doc](https://docs.couchbase.com/server/current/search/search-index-params.html#params) mentions this attribute is required for vector search to return the indexed field
82
- index_dynamic: true,
83
- store_dynamic: true,
84
- // [Doc](https://docs.couchbase.com/server/current/search/search-index-params.html#params) mentions this attribute is required for vector search to return the indexed field
85
- type_field: "_type",
86
- types: {
87
- [`${this.scopeName}.${this.collectionName}`]: {
91
+ mapping: {
92
+ default_analyzer: "standard",
93
+ default_datetime_parser: "dateTimeOptional",
94
+ default_field: "_all",
95
+ default_mapping: {
88
96
  dynamic: true,
89
- enabled: true,
90
- properties: {
91
- embedding: {
92
- enabled: true,
93
- fields: [
94
- {
95
- dims: dimension,
96
- index: true,
97
- name: "embedding",
98
- similarity: DISTANCE_MAPPING[metric],
99
- type: "vector",
100
- vector_index_optimized_for: "recall",
101
- store: true,
102
- // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
103
- docvalues: true,
104
- // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
105
- include_term_vectors: true
106
- // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
107
- }
108
- ]
109
- },
110
- content: {
111
- enabled: true,
112
- fields: [
113
- {
114
- index: true,
115
- name: "content",
116
- store: true,
117
- type: "text"
118
- }
119
- ]
97
+ enabled: false
98
+ },
99
+ default_type: "_default",
100
+ docvalues_dynamic: true,
101
+ // [Doc](https://docs.couchbase.com/server/current/search/search-index-params.html#params) mentions this attribute is required for vector search to return the indexed field
102
+ index_dynamic: true,
103
+ store_dynamic: true,
104
+ // [Doc](https://docs.couchbase.com/server/current/search/search-index-params.html#params) mentions this attribute is required for vector search to return the indexed field
105
+ type_field: "_type",
106
+ types: {
107
+ [`${this.scopeName}.${this.collectionName}`]: {
108
+ dynamic: true,
109
+ enabled: true,
110
+ properties: {
111
+ embedding: {
112
+ enabled: true,
113
+ fields: [
114
+ {
115
+ dims: dimension,
116
+ index: true,
117
+ name: "embedding",
118
+ similarity: DISTANCE_MAPPING[metric],
119
+ type: "vector",
120
+ vector_index_optimized_for: "recall",
121
+ store: true,
122
+ // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
123
+ docvalues: true,
124
+ // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
125
+ include_term_vectors: true
126
+ // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
127
+ }
128
+ ]
129
+ },
130
+ content: {
131
+ enabled: true,
132
+ fields: [
133
+ {
134
+ index: true,
135
+ name: "content",
136
+ store: true,
137
+ type: "text"
138
+ }
139
+ ]
140
+ }
120
141
  }
121
142
  }
122
143
  }
144
+ },
145
+ store: {
146
+ indexType: "scorch",
147
+ segmentVersion: 16
123
148
  }
124
149
  },
125
- store: {
126
- indexType: "scorch",
127
- segmentVersion: 16
150
+ sourceUuid: "",
151
+ sourceParams: {},
152
+ sourceType: "gocbcore",
153
+ planParams: {
154
+ maxPartitionsPerPIndex: 64,
155
+ indexPartitions: 16,
156
+ numReplicas: 0
128
157
  }
129
- },
130
- sourceUuid: "",
131
- sourceParams: {},
132
- sourceType: "gocbcore",
133
- planParams: {
134
- maxPartitionsPerPIndex: 64,
135
- indexPartitions: 16,
136
- numReplicas: 0
137
- }
138
- });
139
- this.vector_dimension = dimension;
158
+ });
159
+ this.vector_dimension = dimension;
160
+ } catch (error) {
161
+ const message = error?.message || error?.toString();
162
+ if (message && message.toLowerCase().includes("index exists")) {
163
+ await this.validateExistingIndex(indexName, dimension, metric);
164
+ return;
165
+ }
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
+ );
179
+ }
140
180
  }
141
- async upsert(params) {
142
- const { vectors, metadata, ids } = params;
143
- await this.getCollection();
144
- if (!vectors || vectors.length === 0) {
145
- throw new Error("No vectors provided");
181
+ async upsert({ vectors, metadata, ids }) {
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
+ }
192
+ }
193
+ }
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]));
209
+ }
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
+ );
146
221
  }
147
- if (this.vector_dimension) {
148
- for (const vector of vectors) {
149
- if (!vector || this.vector_dimension !== vector.length) {
150
- throw new Error("Vector dimension mismatch");
222
+ }
223
+ async query({ indexName, queryVector, topK = 10, includeVector = false }) {
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
+ );
231
+ }
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: ["*"]
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
+ }
151
250
  }
251
+ output.push({
252
+ id: match.id,
253
+ score: match.score || 0,
254
+ metadata: cleanedMetadata
255
+ // Use the cleaned metadata object
256
+ });
152
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
+ );
153
272
  }
154
- const pointIds = ids || vectors.map(() => crypto.randomUUID());
155
- const records = vectors.map((vector, i) => {
156
- const metadataObj = metadata?.[i] || {};
157
- const record = {
158
- embedding: vector,
159
- metadata: metadataObj
160
- };
161
- if (metadataObj.text) {
162
- record.content = metadataObj.text;
163
- }
164
- return record;
165
- });
166
- const allPromises = [];
167
- for (let i = 0; i < records.length; i++) {
168
- allPromises.push(this.collection.upsert(pointIds[i], records[i]));
273
+ }
274
+ async listIndexes() {
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
+ );
169
288
  }
170
- await Promise.all(allPromises);
171
- return pointIds;
172
289
  }
173
- async query(params) {
174
- const { indexName, queryVector, topK = 10, includeVector = false } = params;
175
- await this.getCollection();
176
- const index_stats = await this.describeIndex(indexName);
177
- if (queryVector.length !== index_stats.dimension) {
178
- throw new Error(`Query vector dimension mismatch. Expected ${index_stats.dimension}, got ${queryVector.length}`);
290
+ /**
291
+ * Retrieves statistics about a vector index.
292
+ *
293
+ * @param {string} indexName - The name of the index to describe
294
+ * @returns A promise that resolves to the index statistics including dimension, count and metric
295
+ */
296
+ async describeIndex({ indexName }) {
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
+ );
179
325
  }
180
- let request = SearchRequest.create(
181
- VectorSearch.fromVectorQuery(VectorQuery.create("embedding", queryVector).numCandidates(topK))
182
- );
183
- const results = await this.scope.search(indexName, request, {
184
- fields: ["*"]
185
- });
186
- if (includeVector) {
187
- throw new Error("Including vectors in search results is not yet supported by the Couchbase vector store");
326
+ }
327
+ async deleteIndex({ indexName }) {
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
+ );
188
350
  }
189
- const output = [];
190
- for (const match of results.rows) {
191
- const cleanedMetadata = {};
192
- const fields = match.fields || {};
193
- for (const key in fields) {
194
- if (Object.prototype.hasOwnProperty.call(fields, key)) {
195
- const newKey = key.startsWith("metadata.") ? key.substring("metadata.".length) : key;
196
- cleanedMetadata[newKey] = fields[key];
351
+ }
352
+ /**
353
+ * Updates a vector by its ID with the provided vector and/or metadata.
354
+ * @param indexName - The name of the index containing the vector.
355
+ * @param id - The ID of the vector to update.
356
+ * @param update - An object containing the vector and/or metadata to update.
357
+ * @param update.vector - An optional array of numbers representing the new vector.
358
+ * @param update.metadata - An optional record containing the new metadata.
359
+ * @returns A promise that resolves when the update is complete.
360
+ * @throws Will throw an error if no updates are provided or if the update operation fails.
361
+ */
362
+ async updateVector({ id, update }) {
363
+ try {
364
+ if (!update.vector && !update.metadata) {
365
+ throw new Error("No updates provided");
366
+ }
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`);
197
376
  }
377
+ throw err;
198
378
  }
199
- output.push({
200
- id: match.id,
201
- score: match.score || 0,
202
- metadata: cleanedMetadata
203
- // Use the cleaned metadata object
204
- });
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
+ );
205
397
  }
206
- return output;
207
- }
208
- async listIndexes() {
209
- await this.getCollection();
210
- const indexes = await this.scope.searchIndexes().getAllIndexes();
211
- return indexes?.map((index) => index.name) || [];
212
398
  }
213
- async describeIndex(indexName) {
214
- await this.getCollection();
215
- if (!(await this.listIndexes()).includes(indexName)) {
216
- throw new Error(`Index ${indexName} does not exist`);
399
+ /**
400
+ * Deletes a vector by its ID.
401
+ * @param indexName - The name of the index containing the vector.
402
+ * @param id - The ID of the vector to delete.
403
+ * @returns A promise that resolves when the deletion is complete.
404
+ * @throws Will throw an error if the deletion operation fails.
405
+ */
406
+ async deleteVector({ id }) {
407
+ try {
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;
416
+ }
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
+ );
217
430
  }
218
- const index = await this.scope.searchIndexes().getIndex(indexName);
219
- const dimensions = index.params.mapping?.types?.[`${this.scopeName}.${this.collectionName}`]?.properties?.embedding?.fields?.[0]?.dims;
220
- const count = -1;
221
- const metric = index.params.mapping?.types?.[`${this.scopeName}.${this.collectionName}`]?.properties?.embedding?.fields?.[0]?.similarity;
222
- return {
223
- dimension: dimensions,
224
- count,
225
- metric: Object.keys(DISTANCE_MAPPING).find(
226
- (key) => DISTANCE_MAPPING[key] === metric
227
- )
228
- };
229
431
  }
230
- async deleteIndex(indexName) {
231
- await this.getCollection();
232
- if (!(await this.listIndexes()).includes(indexName)) {
233
- throw new Error(`Index ${indexName} does not exist`);
432
+ async disconnect() {
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
+ );
234
447
  }
235
- await this.scope.searchIndexes().dropIndex(indexName);
236
- this.vector_dimension = null;
237
448
  }
238
449
  };
239
450
 
@@ -2,20 +2,20 @@ services:
2
2
  mastra_couchbase_testing:
3
3
  image: couchbase:enterprise-7.6.4
4
4
  ports:
5
- - "8091-8095:8091-8095"
6
- - "11210:11210"
7
- - "9102:9102"
5
+ - '8091-8095:8091-8095'
6
+ - '11210:11210'
7
+ - '9102:9102'
8
8
  expose:
9
- - "8091"
10
- - "8092"
11
- - "8093"
12
- - "8094"
13
- - "8095"
14
- - "9102"
15
- - "11210"
9
+ - '8091'
10
+ - '8092'
11
+ - '8093'
12
+ - '8094'
13
+ - '8095'
14
+ - '9102'
15
+ - '11210'
16
16
  healthcheck: # checks couchbase server is up
17
- test: ["CMD", "curl", "-v", "http://localhost:8091/pools"]
17
+ test: ['CMD', 'curl', '-v', 'http://localhost:8091/pools']
18
18
  interval: 20s
19
19
  timeout: 20s
20
20
  retries: 5
21
- container_name: mastra_couchbase_testing
21
+ container_name: mastra_couchbase_testing