@mastra/turbopuffer 1.2.0 → 1.2.1-alpha.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.
package/dist/index.cjs CHANGED
@@ -1,685 +1,579 @@
1
- 'use strict';
2
-
3
- var error = require('@mastra/core/error');
4
- var storage = require('@mastra/core/storage');
5
- var vector = require('@mastra/core/vector');
6
- var turbopuffer = require('@turbopuffer/turbopuffer');
7
- var filter = require('@mastra/core/vector/filter');
8
-
9
- // src/vector/index.ts
10
- var TurbopufferFilterTranslator = class extends filter.BaseFilterTranslator {
11
- getSupportedOperators() {
12
- return {
13
- ...filter.BaseFilterTranslator.DEFAULT_OPERATORS,
14
- logical: ["$and", "$or"],
15
- array: ["$in", "$nin", "$all"],
16
- element: ["$exists"],
17
- regex: [],
18
- // No regex support in Turbopuffer
19
- custom: []
20
- // No custom operators
21
- };
22
- }
23
- /**
24
- * Map Mastra operators to Turbopuffer operators
25
- */
26
- operatorMap = {
27
- $eq: "Eq",
28
- $ne: "NotEq",
29
- $gt: "Gt",
30
- $gte: "Gte",
31
- $lt: "Lt",
32
- $lte: "Lte",
33
- $in: "In",
34
- $nin: "NotIn"
35
- };
36
- /**
37
- * Convert the Mastra filter to Turbopuffer format
38
- */
39
- translate(filter) {
40
- if (this.isEmpty(filter)) {
41
- return void 0;
42
- }
43
- this.validateFilter(filter);
44
- const result = this.translateNode(filter);
45
- if (!Array.isArray(result) || result.length !== 2 || result[0] !== "And" && result[0] !== "Or") {
46
- return ["And", [result]];
47
- }
48
- return result;
49
- }
50
- /**
51
- * Recursively translate a filter node
52
- */
53
- translateNode(node) {
54
- if (node === null || node === void 0 || Object.keys(node).length === 0) {
55
- return ["And", []];
56
- }
57
- if (this.isPrimitive(node)) {
58
- throw new Error("Direct primitive values not valid in this context for Turbopuffer");
59
- }
60
- if (Array.isArray(node)) {
61
- throw new Error("Direct array values not valid in this context for Turbopuffer");
62
- }
63
- const entries = Object.entries(node);
64
- if (entries.length === 0) {
65
- return ["And", []];
66
- }
67
- const [key, value] = entries[0];
68
- if (key && this.isLogicalOperator(key)) {
69
- return this.translateLogical(key, value);
70
- }
71
- if (entries.length > 1) {
72
- const conditions = entries.map(([field, fieldValue]) => this.translateFieldCondition(field, fieldValue));
73
- return ["And", conditions];
74
- }
75
- return this.translateFieldCondition(key, value);
76
- }
77
- /**
78
- * Translate a field condition
79
- */
80
- translateFieldCondition(field, value) {
81
- if (value instanceof Date) {
82
- return [field, "Eq", this.normalizeValue(value)];
83
- }
84
- if (this.isPrimitive(value)) {
85
- return [field, "Eq", this.normalizeValue(value)];
86
- }
87
- if (Array.isArray(value)) {
88
- return [field, "In", this.normalizeArrayValues(value)];
89
- }
90
- if (typeof value === "object" && value !== null) {
91
- const operators = Object.keys(value);
92
- if (operators.length > 1) {
93
- const allOperators = operators.every((op2) => this.isOperator(op2));
94
- if (allOperators) {
95
- const conditions = operators.map((op2) => this.translateOperator(field, op2, value[op2]));
96
- return ["And", conditions];
97
- } else {
98
- const conditions = operators.map((op2) => {
99
- const nestedField = `${field}.${op2}`;
100
- return this.translateFieldCondition(nestedField, value[op2]);
101
- });
102
- return ["And", conditions];
103
- }
104
- }
105
- const op = operators[0];
106
- if (op && this.isOperator(op)) {
107
- return this.translateOperator(field, op, value[op]);
108
- }
109
- if (op && !this.isOperator(op)) {
110
- const nestedField = `${field}.${op}`;
111
- return this.translateFieldCondition(nestedField, value[op]);
112
- }
113
- }
114
- throw new Error(`Unsupported filter format for field: ${field}`);
115
- }
116
- /**
117
- * Translate a logical operator
118
- */
119
- translateLogical(operator, conditions) {
120
- const logicalOp = operator === "$and" ? "And" : "Or";
121
- if (!Array.isArray(conditions)) {
122
- throw new Error(`Logical operator ${operator} requires an array of conditions`);
123
- }
124
- const translatedConditions = conditions.map((condition) => {
125
- if (typeof condition !== "object" || condition === null) {
126
- throw new Error(`Invalid condition for logical operator ${operator}`);
127
- }
128
- return this.translateNode(condition);
129
- });
130
- return [logicalOp, translatedConditions];
131
- }
132
- /**
133
- * Translate a specific operator
134
- */
135
- translateOperator(field, operator, value) {
136
- if (operator && this.operatorMap[operator]) {
137
- return [field, this.operatorMap[operator], this.normalizeValue(value)];
138
- }
139
- switch (operator) {
140
- case "$exists":
141
- return value ? [field, "NotEq", null] : [field, "Eq", null];
142
- case "$all":
143
- if (!Array.isArray(value) || value.length === 0) {
144
- throw new Error("$all operator requires a non-empty array");
145
- }
146
- const allConditions = value.map((item) => [field, "In", [this.normalizeValue(item)]]);
147
- return ["And", allConditions];
148
- default:
149
- throw new Error(`Unsupported operator: ${operator || "undefined"}`);
150
- }
151
- }
152
- /**
153
- * Normalize a value for comparison operations
154
- */
155
- normalizeValue(value) {
156
- if (value instanceof Date) {
157
- return value.toISOString();
158
- }
159
- return value;
160
- }
161
- /**
162
- * Normalize array values
163
- */
164
- normalizeArrayValues(values) {
165
- return values.map((value) => this.normalizeValue(value));
166
- }
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _mastra_core_error = require("@mastra/core/error");
3
+ let _mastra_core_storage = require("@mastra/core/storage");
4
+ let _mastra_core_vector = require("@mastra/core/vector");
5
+ let _turbopuffer_turbopuffer = require("@turbopuffer/turbopuffer");
6
+ let _mastra_core_vector_filter = require("@mastra/core/vector/filter");
7
+ //#region src/vector/filter.ts
8
+ /**
9
+ * Translator for converting Mastra filters to Turbopuffer format
10
+ *
11
+ * Mastra filters: { field: { $gt: 10 } }
12
+ * Turbopuffer filters: ["And", [["field", "Gt", 10]]]
13
+ */
14
+ var TurbopufferFilterTranslator = class extends _mastra_core_vector_filter.BaseFilterTranslator {
15
+ getSupportedOperators() {
16
+ return {
17
+ ..._mastra_core_vector_filter.BaseFilterTranslator.DEFAULT_OPERATORS,
18
+ logical: ["$and", "$or"],
19
+ array: [
20
+ "$in",
21
+ "$nin",
22
+ "$all"
23
+ ],
24
+ element: ["$exists"],
25
+ regex: [],
26
+ custom: []
27
+ };
28
+ }
29
+ /**
30
+ * Map Mastra operators to Turbopuffer operators
31
+ */
32
+ operatorMap = {
33
+ $eq: "Eq",
34
+ $ne: "NotEq",
35
+ $gt: "Gt",
36
+ $gte: "Gte",
37
+ $lt: "Lt",
38
+ $lte: "Lte",
39
+ $in: "In",
40
+ $nin: "NotIn"
41
+ };
42
+ /**
43
+ * Convert the Mastra filter to Turbopuffer format
44
+ */
45
+ translate(filter) {
46
+ if (this.isEmpty(filter)) return;
47
+ this.validateFilter(filter);
48
+ const result = this.translateNode(filter);
49
+ if (!Array.isArray(result) || result.length !== 2 || result[0] !== "And" && result[0] !== "Or") return ["And", [result]];
50
+ return result;
51
+ }
52
+ /**
53
+ * Recursively translate a filter node
54
+ */
55
+ translateNode(node) {
56
+ if (node === null || node === void 0 || Object.keys(node).length === 0) return ["And", []];
57
+ if (this.isPrimitive(node)) throw new Error("Direct primitive values not valid in this context for Turbopuffer");
58
+ if (Array.isArray(node)) throw new Error("Direct array values not valid in this context for Turbopuffer");
59
+ const entries = Object.entries(node);
60
+ if (entries.length === 0) return ["And", []];
61
+ const [key, value] = entries[0];
62
+ if (key && this.isLogicalOperator(key)) return this.translateLogical(key, value);
63
+ if (entries.length > 1) return ["And", entries.map(([field, fieldValue]) => this.translateFieldCondition(field, fieldValue))];
64
+ return this.translateFieldCondition(key, value);
65
+ }
66
+ /**
67
+ * Translate a field condition
68
+ */
69
+ translateFieldCondition(field, value) {
70
+ if (value instanceof Date) return [
71
+ field,
72
+ "Eq",
73
+ this.normalizeValue(value)
74
+ ];
75
+ if (this.isPrimitive(value)) return [
76
+ field,
77
+ "Eq",
78
+ this.normalizeValue(value)
79
+ ];
80
+ if (Array.isArray(value)) return [
81
+ field,
82
+ "In",
83
+ this.normalizeArrayValues(value)
84
+ ];
85
+ if (typeof value === "object" && value !== null) {
86
+ const operators = Object.keys(value);
87
+ if (operators.length > 1) if (operators.every((op) => this.isOperator(op))) return ["And", operators.map((op) => this.translateOperator(field, op, value[op]))];
88
+ else return ["And", operators.map((op) => {
89
+ const nestedField = `${field}.${op}`;
90
+ return this.translateFieldCondition(nestedField, value[op]);
91
+ })];
92
+ const op = operators[0];
93
+ if (op && this.isOperator(op)) return this.translateOperator(field, op, value[op]);
94
+ if (op && !this.isOperator(op)) {
95
+ const nestedField = `${field}.${op}`;
96
+ return this.translateFieldCondition(nestedField, value[op]);
97
+ }
98
+ }
99
+ throw new Error(`Unsupported filter format for field: ${field}`);
100
+ }
101
+ /**
102
+ * Translate a logical operator
103
+ */
104
+ translateLogical(operator, conditions) {
105
+ const logicalOp = operator === "$and" ? "And" : "Or";
106
+ if (!Array.isArray(conditions)) throw new Error(`Logical operator ${operator} requires an array of conditions`);
107
+ return [logicalOp, conditions.map((condition) => {
108
+ if (typeof condition !== "object" || condition === null) throw new Error(`Invalid condition for logical operator ${operator}`);
109
+ return this.translateNode(condition);
110
+ })];
111
+ }
112
+ /**
113
+ * Translate a specific operator
114
+ */
115
+ translateOperator(field, operator, value) {
116
+ if (operator && this.operatorMap[operator]) return [
117
+ field,
118
+ this.operatorMap[operator],
119
+ this.normalizeValue(value)
120
+ ];
121
+ switch (operator) {
122
+ case "$exists": return value ? [
123
+ field,
124
+ "NotEq",
125
+ null
126
+ ] : [
127
+ field,
128
+ "Eq",
129
+ null
130
+ ];
131
+ case "$all":
132
+ if (!Array.isArray(value) || value.length === 0) throw new Error("$all operator requires a non-empty array");
133
+ return ["And", value.map((item) => [
134
+ field,
135
+ "In",
136
+ [this.normalizeValue(item)]
137
+ ])];
138
+ default: throw new Error(`Unsupported operator: ${operator || "undefined"}`);
139
+ }
140
+ }
141
+ /**
142
+ * Normalize a value for comparison operations
143
+ */
144
+ normalizeValue(value) {
145
+ if (value instanceof Date) return value.toISOString();
146
+ return value;
147
+ }
148
+ /**
149
+ * Normalize array values
150
+ */
151
+ normalizeArrayValues(values) {
152
+ return values.map((value) => this.normalizeValue(value));
153
+ }
167
154
  };
168
-
169
- // src/vector/index.ts
170
- var TurbopufferVector = class extends vector.MastraVector {
171
- client;
172
- filterTranslator;
173
- // There is no explicit create index operation in Turbopuffer, so just register that
174
- // someone has called createIndex() and verify that subsequent upsert calls are consistent
175
- // with how the index was "created"
176
- createIndexCache = /* @__PURE__ */ new Map();
177
- opts;
178
- constructor(opts) {
179
- super({ id: opts.id });
180
- this.filterTranslator = new TurbopufferFilterTranslator();
181
- this.opts = opts;
182
- this.client = new turbopuffer.Turbopuffer(opts);
183
- }
184
- async createIndex({ indexName, dimension, metric }) {
185
- metric = metric ?? "cosine";
186
- let distanceMetric = "cosine_distance";
187
- try {
188
- if (this.createIndexCache.has(indexName)) {
189
- const expected = this.createIndexCache.get(indexName);
190
- if (dimension !== expected.dimension || metric !== expected.metric) {
191
- throw new Error(
192
- `createIndex() called more than once with inconsistent inputs. Index ${indexName} expected dimensions=${expected.dimension} and metric=${expected.metric} but got dimensions=${dimension} and metric=${metric}`
193
- );
194
- }
195
- return;
196
- }
197
- if (dimension <= 0) {
198
- throw new Error("Dimension must be a positive integer");
199
- }
200
- switch (metric) {
201
- case "cosine":
202
- distanceMetric = "cosine_distance";
203
- break;
204
- case "euclidean":
205
- distanceMetric = "euclidean_squared";
206
- break;
207
- case "dotproduct":
208
- throw new Error("dotproduct is not supported in Turbopuffer");
209
- }
210
- } catch (error$1) {
211
- throw new error.MastraError(
212
- {
213
- id: storage.createVectorErrorId("TURBOPUFFER", "CREATE_INDEX", "INVALID_ARGS"),
214
- domain: error.ErrorDomain.STORAGE,
215
- category: error.ErrorCategory.USER,
216
- details: { indexName, dimension, metric }
217
- },
218
- error$1
219
- );
220
- }
221
- this.createIndexCache.set(indexName, {
222
- indexName,
223
- dimension,
224
- metric,
225
- tpufDistanceMetric: distanceMetric
226
- });
227
- }
228
- async upsert({ indexName, vectors, metadata, ids }) {
229
- let index;
230
- let createIndex;
231
- try {
232
- if (vectors.length === 0) {
233
- throw new Error("upsert() called with empty vectors");
234
- }
235
- index = this.client.namespace(indexName);
236
- createIndex = this.createIndexCache.get(indexName);
237
- if (!createIndex) {
238
- throw new Error(`createIndex() not called for this index`);
239
- }
240
- } catch (error$1) {
241
- throw new error.MastraError(
242
- {
243
- id: storage.createVectorErrorId("TURBOPUFFER", "UPSERT", "INVALID_ARGS"),
244
- domain: error.ErrorDomain.STORAGE,
245
- category: error.ErrorCategory.USER,
246
- details: { indexName }
247
- },
248
- error$1
249
- );
250
- }
251
- try {
252
- const distanceMetric = createIndex.tpufDistanceMetric;
253
- const vectorIds = ids || vectors.map(() => crypto.randomUUID());
254
- const records = vectors.map((vector, i) => ({
255
- id: vectorIds[i],
256
- vector,
257
- ...metadata?.[i] || {}
258
- }));
259
- const batchSize = 100;
260
- for (let i = 0; i < records.length; i += batchSize) {
261
- const batch = records.slice(i, i + batchSize);
262
- const writeOptions = {
263
- upsert_rows: batch,
264
- distance_metric: distanceMetric
265
- };
266
- const schemaConfig = this.opts.schemaConfigForIndex?.(indexName);
267
- if (schemaConfig) {
268
- writeOptions.schema = schemaConfig.schema;
269
- if (vectors[0]?.length !== schemaConfig.dimensions) {
270
- throw new Error(
271
- `Turbopuffer index ${indexName} was configured with dimensions=${schemaConfig.dimensions} but attempting to upsert vectors[0].length=${vectors[0]?.length}`
272
- );
273
- }
274
- }
275
- await index.write(writeOptions);
276
- }
277
- return vectorIds;
278
- } catch (error$1) {
279
- throw new error.MastraError(
280
- {
281
- id: storage.createVectorErrorId("TURBOPUFFER", "UPSERT", "FAILED"),
282
- domain: error.ErrorDomain.STORAGE,
283
- category: error.ErrorCategory.THIRD_PARTY,
284
- details: { indexName }
285
- },
286
- error$1
287
- );
288
- }
289
- }
290
- async query({
291
- indexName,
292
- queryVector,
293
- topK,
294
- filter,
295
- includeVector,
296
- consistency
297
- }) {
298
- if (!queryVector) {
299
- throw new error.MastraError({
300
- id: storage.createVectorErrorId("TURBOPUFFER", "QUERY", "MISSING_VECTOR"),
301
- text: "queryVector is required for Turbopuffer queries. Metadata-only queries are not supported by this vector store.",
302
- domain: error.ErrorDomain.STORAGE,
303
- category: error.ErrorCategory.USER,
304
- details: { indexName }
305
- });
306
- }
307
- let createIndex;
308
- try {
309
- const schemaConfig = this.opts.schemaConfigForIndex?.(indexName);
310
- if (schemaConfig) {
311
- if (queryVector.length !== schemaConfig.dimensions) {
312
- throw new Error(
313
- `Turbopuffer index ${indexName} was configured with dimensions=${schemaConfig.dimensions} but attempting to query with queryVector.length=${queryVector.length}`
314
- );
315
- }
316
- }
317
- createIndex = this.createIndexCache.get(indexName);
318
- if (!createIndex) {
319
- throw new Error(`createIndex() not called for this index`);
320
- }
321
- } catch (error$1) {
322
- throw new error.MastraError(
323
- {
324
- id: storage.createVectorErrorId("TURBOPUFFER", "QUERY", "INVALID_ARGS"),
325
- domain: error.ErrorDomain.STORAGE,
326
- category: error.ErrorCategory.USER,
327
- details: { indexName }
328
- },
329
- error$1
330
- );
331
- }
332
- const distanceMetric = createIndex.tpufDistanceMetric;
333
- try {
334
- const index = this.client.namespace(indexName);
335
- const translatedFilter = this.filterTranslator.translate(filter);
336
- const results = await index.query({
337
- distance_metric: distanceMetric,
338
- rank_by: ["vector", "ANN", queryVector],
339
- top_k: topK,
340
- filters: translatedFilter,
341
- vector_encoding: includeVector ? "float" : void 0,
342
- include_attributes: true,
343
- consistency: { level: consistency ?? this.opts.consistency ?? "strong" }
344
- });
345
- return (results.rows ?? []).map((item) => {
346
- const { id, vector, $dist, ...metadata } = item;
347
- return {
348
- id: String(id),
349
- score: typeof $dist === "number" ? $dist : 0,
350
- metadata,
351
- ...includeVector && Array.isArray(vector) ? { vector } : {}
352
- };
353
- });
354
- } catch (error$1) {
355
- throw new error.MastraError(
356
- {
357
- id: storage.createVectorErrorId("TURBOPUFFER", "QUERY", "FAILED"),
358
- domain: error.ErrorDomain.STORAGE,
359
- category: error.ErrorCategory.THIRD_PARTY,
360
- details: { indexName }
361
- },
362
- error$1
363
- );
364
- }
365
- }
366
- async listIndexes() {
367
- try {
368
- const namespacesResult = await this.client.namespaces({});
369
- return namespacesResult.namespaces.map((namespace) => namespace.id);
370
- } catch (error$1) {
371
- throw new error.MastraError(
372
- {
373
- id: storage.createVectorErrorId("TURBOPUFFER", "LIST_INDEXES", "FAILED"),
374
- domain: error.ErrorDomain.STORAGE,
375
- category: error.ErrorCategory.THIRD_PARTY
376
- },
377
- error$1
378
- );
379
- }
380
- }
381
- /**
382
- * Retrieves statistics about a vector index.
383
- *
384
- * @param {string} indexName - The name of the index to describe
385
- * @returns A promise that resolves to the index statistics including dimension, count and metric
386
- */
387
- async describeIndex({ indexName }) {
388
- try {
389
- const namespace = this.client.namespace(indexName);
390
- const metadata = await namespace.metadata();
391
- const createIndex = this.createIndexCache.get(indexName);
392
- if (!createIndex) {
393
- throw new Error(`createIndex() not called for this index`);
394
- }
395
- const dimension = createIndex.dimension;
396
- const count = metadata.approx_row_count;
397
- return {
398
- dimension,
399
- count,
400
- metric: createIndex.metric
401
- };
402
- } catch (error$1) {
403
- throw new error.MastraError(
404
- {
405
- id: storage.createVectorErrorId("TURBOPUFFER", "DESCRIBE_INDEX", "FAILED"),
406
- domain: error.ErrorDomain.STORAGE,
407
- category: error.ErrorCategory.THIRD_PARTY,
408
- details: { indexName }
409
- },
410
- error$1
411
- );
412
- }
413
- }
414
- async deleteIndex({ indexName }) {
415
- try {
416
- const namespace = this.client.namespace(indexName);
417
- await namespace.deleteAll();
418
- this.createIndexCache.delete(indexName);
419
- } catch (error$1) {
420
- throw new error.MastraError(
421
- {
422
- id: storage.createVectorErrorId("TURBOPUFFER", "DELETE_INDEX", "FAILED"),
423
- domain: error.ErrorDomain.STORAGE,
424
- category: error.ErrorCategory.THIRD_PARTY,
425
- details: { indexName }
426
- },
427
- error$1
428
- );
429
- }
430
- }
431
- /**
432
- * Updates a vector by its ID or filter with the provided vector and/or metadata.
433
- * @param indexName - The name of the index containing the vector.
434
- * @param id - The ID of the vector to update.
435
- * @param filter - The filter to match vectors to update.
436
- * @param update - An object containing the vector and/or metadata to update.
437
- * @param update.vector - An optional array of numbers representing the new vector.
438
- * @param update.metadata - An optional record containing the new metadata.
439
- * @returns A promise that resolves when the update is complete.
440
- * @throws Will throw an error if no updates are provided or if the update operation fails.
441
- */
442
- async updateVector({ indexName, id, filter, update }) {
443
- if (id && filter) {
444
- throw new error.MastraError({
445
- id: storage.createVectorErrorId("TURBOPUFFER", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
446
- domain: error.ErrorDomain.STORAGE,
447
- category: error.ErrorCategory.USER,
448
- text: "id and filter are mutually exclusive",
449
- details: { indexName }
450
- });
451
- }
452
- if (!id && !filter) {
453
- throw new error.MastraError({
454
- id: storage.createVectorErrorId("TURBOPUFFER", "UPDATE_VECTOR", "NO_TARGET"),
455
- domain: error.ErrorDomain.STORAGE,
456
- category: error.ErrorCategory.USER,
457
- text: "Either id or filter must be provided",
458
- details: { indexName }
459
- });
460
- }
461
- if (!update.vector && !update.metadata) {
462
- throw new error.MastraError({
463
- id: storage.createVectorErrorId("TURBOPUFFER", "UPDATE_VECTOR", "NO_PAYLOAD"),
464
- domain: error.ErrorDomain.STORAGE,
465
- category: error.ErrorCategory.USER,
466
- text: "No update data provided",
467
- details: { indexName, ...id && { id } }
468
- });
469
- }
470
- let namespace;
471
- let createIndex;
472
- let distanceMetric;
473
- try {
474
- namespace = this.client.namespace(indexName);
475
- createIndex = this.createIndexCache.get(indexName);
476
- if (!createIndex) {
477
- throw new Error(`createIndex() not called for this index`);
478
- }
479
- distanceMetric = createIndex.tpufDistanceMetric;
480
- } catch (error$1) {
481
- throw new error.MastraError(
482
- {
483
- id: storage.createVectorErrorId("TURBOPUFFER", "UPDATE_VECTOR", "INVALID_ARGS"),
484
- domain: error.ErrorDomain.STORAGE,
485
- category: error.ErrorCategory.USER,
486
- details: { indexName }
487
- },
488
- error$1
489
- );
490
- }
491
- try {
492
- let idsToUpdate = [];
493
- if (id) {
494
- idsToUpdate = [id];
495
- } else if (filter) {
496
- if (Object.keys(filter).length === 0) {
497
- throw new error.MastraError({
498
- id: storage.createVectorErrorId("TURBOPUFFER", "UPDATE_VECTOR", "EMPTY_FILTER"),
499
- domain: error.ErrorDomain.STORAGE,
500
- category: error.ErrorCategory.USER,
501
- text: "Filter cannot be an empty object",
502
- details: { indexName }
503
- });
504
- }
505
- const dummyVector = new Array(createIndex.dimension).fill(1 / Math.sqrt(createIndex.dimension));
506
- const translatedFilter = this.filterTranslator.translate(filter);
507
- const results = await namespace.query({
508
- rank_by: ["vector", "ANN", dummyVector],
509
- top_k: 1e4,
510
- // Get all matching vectors
511
- filters: translatedFilter,
512
- vector_encoding: update.vector ? void 0 : "float",
513
- // Only fetch vectors if we're not replacing them
514
- include_attributes: true
515
- });
516
- const rows = results.rows ?? [];
517
- idsToUpdate = rows.map((r) => String(r.id));
518
- if (!update.vector || !update.metadata) {
519
- for (const result of rows) {
520
- const { id: resultId, vector, $dist, ...metadata } = result;
521
- const record = { id: resultId };
522
- if (update.vector) {
523
- record.vector = update.vector;
524
- } else if (Array.isArray(vector)) {
525
- record.vector = vector;
526
- }
527
- if (update.metadata) {
528
- Object.assign(record, update.metadata);
529
- } else {
530
- Object.assign(record, metadata);
531
- }
532
- await namespace.write({
533
- upsert_rows: [record],
534
- distance_metric: distanceMetric
535
- });
536
- }
537
- return;
538
- }
539
- }
540
- if (idsToUpdate.length === 0) {
541
- this.logger.info(`No vectors matched the criteria for update in ${indexName}`);
542
- return;
543
- }
544
- const records = idsToUpdate.map((vecId) => ({
545
- id: vecId,
546
- ...update.vector ? { vector: update.vector } : {},
547
- ...update.metadata || {}
548
- }));
549
- const batchSize = 1e3;
550
- for (let i = 0; i < records.length; i += batchSize) {
551
- const batch = records.slice(i, i + batchSize);
552
- await namespace.write({
553
- upsert_rows: batch,
554
- distance_metric: distanceMetric
555
- });
556
- }
557
- } catch (error$1) {
558
- if (error$1 instanceof error.MastraError) throw error$1;
559
- throw new error.MastraError(
560
- {
561
- id: storage.createVectorErrorId("TURBOPUFFER", "UPDATE_VECTOR", "FAILED"),
562
- domain: error.ErrorDomain.STORAGE,
563
- category: error.ErrorCategory.THIRD_PARTY,
564
- details: {
565
- indexName,
566
- ...id && { id },
567
- ...filter && { filter: JSON.stringify(filter) }
568
- }
569
- },
570
- error$1
571
- );
572
- }
573
- }
574
- /**
575
- * Deletes a vector by its ID.
576
- * @param indexName - The name of the index containing the vector.
577
- * @param id - The ID of the vector to delete.
578
- * @returns A promise that resolves when the deletion is complete.
579
- * @throws Will throw an error if the deletion operation fails.
580
- */
581
- async deleteVector({ indexName, id }) {
582
- try {
583
- const namespace = this.client.namespace(indexName);
584
- await namespace.write({ deletes: [id] });
585
- } catch (error$1) {
586
- throw new error.MastraError(
587
- {
588
- id: storage.createVectorErrorId("TURBOPUFFER", "DELETE_VECTOR", "FAILED"),
589
- domain: error.ErrorDomain.STORAGE,
590
- category: error.ErrorCategory.THIRD_PARTY,
591
- details: { indexName }
592
- },
593
- error$1
594
- );
595
- }
596
- }
597
- async deleteVectors({ indexName, filter, ids }) {
598
- if (ids && filter) {
599
- throw new error.MastraError({
600
- id: storage.createVectorErrorId("TURBOPUFFER", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
601
- domain: error.ErrorDomain.STORAGE,
602
- category: error.ErrorCategory.USER,
603
- text: "ids and filter are mutually exclusive",
604
- details: { indexName }
605
- });
606
- }
607
- if (!ids && !filter) {
608
- throw new error.MastraError({
609
- id: storage.createVectorErrorId("TURBOPUFFER", "DELETE_VECTORS", "NO_TARGET"),
610
- domain: error.ErrorDomain.STORAGE,
611
- category: error.ErrorCategory.USER,
612
- text: "Either filter or ids must be provided",
613
- details: { indexName }
614
- });
615
- }
616
- if (ids && ids.length === 0) {
617
- throw new error.MastraError({
618
- id: storage.createVectorErrorId("TURBOPUFFER", "DELETE_VECTORS", "EMPTY_IDS"),
619
- domain: error.ErrorDomain.STORAGE,
620
- category: error.ErrorCategory.USER,
621
- text: "ids array cannot be empty",
622
- details: { indexName }
623
- });
624
- }
625
- if (filter && Object.keys(filter).length === 0) {
626
- throw new error.MastraError({
627
- id: storage.createVectorErrorId("TURBOPUFFER", "DELETE_VECTORS", "EMPTY_FILTER"),
628
- domain: error.ErrorDomain.STORAGE,
629
- category: error.ErrorCategory.USER,
630
- text: "Filter cannot be an empty object",
631
- details: { indexName }
632
- });
633
- }
634
- try {
635
- const namespace = this.client.namespace(indexName);
636
- let idsToDelete = [];
637
- if (ids) {
638
- idsToDelete = ids;
639
- } else if (filter) {
640
- const createIndex = this.createIndexCache.get(indexName);
641
- if (!createIndex) {
642
- throw new Error(`createIndex() not called for this index`);
643
- }
644
- const dummyVector = new Array(createIndex.dimension).fill(1 / Math.sqrt(createIndex.dimension));
645
- const translatedFilter = this.filterTranslator.translate(filter);
646
- const results = await namespace.query({
647
- rank_by: ["vector", "ANN", dummyVector],
648
- top_k: 1e4,
649
- // Get all matching vectors
650
- filters: translatedFilter,
651
- include_attributes: []
652
- });
653
- idsToDelete = (results.rows ?? []).map((r) => String(r.id));
654
- }
655
- if (idsToDelete.length === 0) {
656
- this.logger.info(`No vectors matched the criteria for deletion in ${indexName}`);
657
- return;
658
- }
659
- const batchSize = 1e3;
660
- for (let i = 0; i < idsToDelete.length; i += batchSize) {
661
- const batch = idsToDelete.slice(i, i + batchSize);
662
- await namespace.write({ deletes: batch });
663
- }
664
- } catch (error$1) {
665
- if (error$1 instanceof error.MastraError) throw error$1;
666
- throw new error.MastraError(
667
- {
668
- id: storage.createVectorErrorId("TURBOPUFFER", "DELETE_VECTORS", "FAILED"),
669
- domain: error.ErrorDomain.STORAGE,
670
- category: error.ErrorCategory.THIRD_PARTY,
671
- details: {
672
- indexName,
673
- ...filter && { filter: JSON.stringify(filter) },
674
- ...ids && { idsCount: ids.length }
675
- }
676
- },
677
- error$1
678
- );
679
- }
680
- }
155
+ //#endregion
156
+ //#region src/vector/index.ts
157
+ var TurbopufferVector = class extends _mastra_core_vector.MastraVector {
158
+ client;
159
+ filterTranslator;
160
+ createIndexCache = /* @__PURE__ */ new Map();
161
+ opts;
162
+ constructor(opts) {
163
+ super({ id: opts.id });
164
+ this.filterTranslator = new TurbopufferFilterTranslator();
165
+ this.opts = opts;
166
+ this.client = new _turbopuffer_turbopuffer.Turbopuffer(opts);
167
+ }
168
+ async createIndex({ indexName, dimension, metric }) {
169
+ metric = metric ?? "cosine";
170
+ let distanceMetric = "cosine_distance";
171
+ try {
172
+ if (this.createIndexCache.has(indexName)) {
173
+ const expected = this.createIndexCache.get(indexName);
174
+ if (dimension !== expected.dimension || metric !== expected.metric) throw new Error(`createIndex() called more than once with inconsistent inputs. Index ${indexName} expected dimensions=${expected.dimension} and metric=${expected.metric} but got dimensions=${dimension} and metric=${metric}`);
175
+ return;
176
+ }
177
+ if (dimension <= 0) throw new Error("Dimension must be a positive integer");
178
+ switch (metric) {
179
+ case "cosine":
180
+ distanceMetric = "cosine_distance";
181
+ break;
182
+ case "euclidean":
183
+ distanceMetric = "euclidean_squared";
184
+ break;
185
+ case "dotproduct": throw new Error("dotproduct is not supported in Turbopuffer");
186
+ }
187
+ } catch (error) {
188
+ throw new _mastra_core_error.MastraError({
189
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "CREATE_INDEX", "INVALID_ARGS"),
190
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
191
+ category: _mastra_core_error.ErrorCategory.USER,
192
+ details: {
193
+ indexName,
194
+ dimension,
195
+ metric
196
+ }
197
+ }, error);
198
+ }
199
+ this.createIndexCache.set(indexName, {
200
+ indexName,
201
+ dimension,
202
+ metric,
203
+ tpufDistanceMetric: distanceMetric
204
+ });
205
+ }
206
+ async upsert({ indexName, vectors, metadata, ids }) {
207
+ let index;
208
+ let createIndex;
209
+ try {
210
+ if (vectors.length === 0) throw new Error("upsert() called with empty vectors");
211
+ index = this.client.namespace(indexName);
212
+ createIndex = this.createIndexCache.get(indexName);
213
+ if (!createIndex) throw new Error(`createIndex() not called for this index`);
214
+ } catch (error) {
215
+ throw new _mastra_core_error.MastraError({
216
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "UPSERT", "INVALID_ARGS"),
217
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
218
+ category: _mastra_core_error.ErrorCategory.USER,
219
+ details: { indexName }
220
+ }, error);
221
+ }
222
+ try {
223
+ const distanceMetric = createIndex.tpufDistanceMetric;
224
+ const vectorIds = ids || vectors.map(() => crypto.randomUUID());
225
+ const records = vectors.map((vector, i) => ({
226
+ id: vectorIds[i],
227
+ vector,
228
+ ...metadata?.[i] || {}
229
+ }));
230
+ const batchSize = 100;
231
+ for (let i = 0; i < records.length; i += batchSize) {
232
+ const writeOptions = {
233
+ upsert_rows: records.slice(i, i + batchSize),
234
+ distance_metric: distanceMetric
235
+ };
236
+ const schemaConfig = this.opts.schemaConfigForIndex?.(indexName);
237
+ if (schemaConfig) {
238
+ writeOptions.schema = schemaConfig.schema;
239
+ if (vectors[0]?.length !== schemaConfig.dimensions) throw new Error(`Turbopuffer index ${indexName} was configured with dimensions=${schemaConfig.dimensions} but attempting to upsert vectors[0].length=${vectors[0]?.length}`);
240
+ }
241
+ await index.write(writeOptions);
242
+ }
243
+ return vectorIds;
244
+ } catch (error) {
245
+ throw new _mastra_core_error.MastraError({
246
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "UPSERT", "FAILED"),
247
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
248
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
249
+ details: { indexName }
250
+ }, error);
251
+ }
252
+ }
253
+ async query({ indexName, queryVector, topK, filter, includeVector, consistency }) {
254
+ if (!queryVector) throw new _mastra_core_error.MastraError({
255
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "QUERY", "MISSING_VECTOR"),
256
+ text: "queryVector is required for Turbopuffer queries. Metadata-only queries are not supported by this vector store.",
257
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
258
+ category: _mastra_core_error.ErrorCategory.USER,
259
+ details: { indexName }
260
+ });
261
+ let createIndex;
262
+ try {
263
+ const schemaConfig = this.opts.schemaConfigForIndex?.(indexName);
264
+ if (schemaConfig) {
265
+ if (queryVector.length !== schemaConfig.dimensions) throw new Error(`Turbopuffer index ${indexName} was configured with dimensions=${schemaConfig.dimensions} but attempting to query with queryVector.length=${queryVector.length}`);
266
+ }
267
+ createIndex = this.createIndexCache.get(indexName);
268
+ if (!createIndex) throw new Error(`createIndex() not called for this index`);
269
+ } catch (error) {
270
+ throw new _mastra_core_error.MastraError({
271
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "QUERY", "INVALID_ARGS"),
272
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
273
+ category: _mastra_core_error.ErrorCategory.USER,
274
+ details: { indexName }
275
+ }, error);
276
+ }
277
+ const distanceMetric = createIndex.tpufDistanceMetric;
278
+ try {
279
+ const index = this.client.namespace(indexName);
280
+ const translatedFilter = this.filterTranslator.translate(filter);
281
+ return ((await index.query({
282
+ distance_metric: distanceMetric,
283
+ rank_by: [
284
+ "vector",
285
+ "ANN",
286
+ queryVector
287
+ ],
288
+ top_k: topK,
289
+ filters: translatedFilter,
290
+ vector_encoding: includeVector ? "float" : void 0,
291
+ include_attributes: true,
292
+ consistency: { level: consistency ?? this.opts.consistency ?? "strong" }
293
+ })).rows ?? []).map((item) => {
294
+ const { id, vector, $dist, ...metadata } = item;
295
+ return {
296
+ id: String(id),
297
+ score: typeof $dist === "number" ? $dist : 0,
298
+ metadata,
299
+ ...includeVector && Array.isArray(vector) ? { vector } : {}
300
+ };
301
+ });
302
+ } catch (error) {
303
+ throw new _mastra_core_error.MastraError({
304
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "QUERY", "FAILED"),
305
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
306
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
307
+ details: { indexName }
308
+ }, error);
309
+ }
310
+ }
311
+ async listIndexes() {
312
+ try {
313
+ return (await this.client.namespaces({})).namespaces.map((namespace) => namespace.id);
314
+ } catch (error) {
315
+ throw new _mastra_core_error.MastraError({
316
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "LIST_INDEXES", "FAILED"),
317
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
318
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY
319
+ }, error);
320
+ }
321
+ }
322
+ /**
323
+ * Retrieves statistics about a vector index.
324
+ *
325
+ * @param {string} indexName - The name of the index to describe
326
+ * @returns A promise that resolves to the index statistics including dimension, count and metric
327
+ */
328
+ async describeIndex({ indexName }) {
329
+ try {
330
+ const metadata = await this.client.namespace(indexName).metadata();
331
+ const createIndex = this.createIndexCache.get(indexName);
332
+ if (!createIndex) throw new Error(`createIndex() not called for this index`);
333
+ return {
334
+ dimension: createIndex.dimension,
335
+ count: metadata.approx_row_count,
336
+ metric: createIndex.metric
337
+ };
338
+ } catch (error) {
339
+ throw new _mastra_core_error.MastraError({
340
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "DESCRIBE_INDEX", "FAILED"),
341
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
342
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
343
+ details: { indexName }
344
+ }, error);
345
+ }
346
+ }
347
+ async deleteIndex({ indexName }) {
348
+ try {
349
+ await this.client.namespace(indexName).deleteAll();
350
+ this.createIndexCache.delete(indexName);
351
+ } catch (error) {
352
+ throw new _mastra_core_error.MastraError({
353
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "DELETE_INDEX", "FAILED"),
354
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
355
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
356
+ details: { indexName }
357
+ }, error);
358
+ }
359
+ }
360
+ /**
361
+ * Updates a vector by its ID or filter with the provided vector and/or metadata.
362
+ * @param indexName - The name of the index containing the vector.
363
+ * @param id - The ID of the vector to update.
364
+ * @param filter - The filter to match vectors to update.
365
+ * @param update - An object containing the vector and/or metadata to update.
366
+ * @param update.vector - An optional array of numbers representing the new vector.
367
+ * @param update.metadata - An optional record containing the new metadata.
368
+ * @returns A promise that resolves when the update is complete.
369
+ * @throws Will throw an error if no updates are provided or if the update operation fails.
370
+ */
371
+ async updateVector({ indexName, id, filter, update }) {
372
+ if (id && filter) throw new _mastra_core_error.MastraError({
373
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
374
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
375
+ category: _mastra_core_error.ErrorCategory.USER,
376
+ text: "id and filter are mutually exclusive",
377
+ details: { indexName }
378
+ });
379
+ if (!id && !filter) throw new _mastra_core_error.MastraError({
380
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "UPDATE_VECTOR", "NO_TARGET"),
381
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
382
+ category: _mastra_core_error.ErrorCategory.USER,
383
+ text: "Either id or filter must be provided",
384
+ details: { indexName }
385
+ });
386
+ if (!update.vector && !update.metadata) throw new _mastra_core_error.MastraError({
387
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "UPDATE_VECTOR", "NO_PAYLOAD"),
388
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
389
+ category: _mastra_core_error.ErrorCategory.USER,
390
+ text: "No update data provided",
391
+ details: {
392
+ indexName,
393
+ ...id && { id }
394
+ }
395
+ });
396
+ let namespace;
397
+ let createIndex;
398
+ let distanceMetric;
399
+ try {
400
+ namespace = this.client.namespace(indexName);
401
+ createIndex = this.createIndexCache.get(indexName);
402
+ if (!createIndex) throw new Error(`createIndex() not called for this index`);
403
+ distanceMetric = createIndex.tpufDistanceMetric;
404
+ } catch (error) {
405
+ throw new _mastra_core_error.MastraError({
406
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "UPDATE_VECTOR", "INVALID_ARGS"),
407
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
408
+ category: _mastra_core_error.ErrorCategory.USER,
409
+ details: { indexName }
410
+ }, error);
411
+ }
412
+ try {
413
+ let idsToUpdate = [];
414
+ if (id) idsToUpdate = [id];
415
+ else if (filter) {
416
+ if (Object.keys(filter).length === 0) throw new _mastra_core_error.MastraError({
417
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "UPDATE_VECTOR", "EMPTY_FILTER"),
418
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
419
+ category: _mastra_core_error.ErrorCategory.USER,
420
+ text: "Filter cannot be an empty object",
421
+ details: { indexName }
422
+ });
423
+ const dummyVector = new Array(createIndex.dimension).fill(1 / Math.sqrt(createIndex.dimension));
424
+ const translatedFilter = this.filterTranslator.translate(filter);
425
+ const rows = (await namespace.query({
426
+ rank_by: [
427
+ "vector",
428
+ "ANN",
429
+ dummyVector
430
+ ],
431
+ top_k: 1e4,
432
+ filters: translatedFilter,
433
+ vector_encoding: update.vector ? void 0 : "float",
434
+ include_attributes: true
435
+ })).rows ?? [];
436
+ idsToUpdate = rows.map((r) => String(r.id));
437
+ if (!update.vector || !update.metadata) {
438
+ for (const result of rows) {
439
+ const { id: resultId, vector, $dist, ...metadata } = result;
440
+ const record = { id: resultId };
441
+ if (update.vector) record.vector = update.vector;
442
+ else if (Array.isArray(vector)) record.vector = vector;
443
+ if (update.metadata) Object.assign(record, update.metadata);
444
+ else Object.assign(record, metadata);
445
+ await namespace.write({
446
+ upsert_rows: [record],
447
+ distance_metric: distanceMetric
448
+ });
449
+ }
450
+ return;
451
+ }
452
+ }
453
+ if (idsToUpdate.length === 0) {
454
+ this.logger.info(`No vectors matched the criteria for update in ${indexName}`);
455
+ return;
456
+ }
457
+ const records = idsToUpdate.map((vecId) => ({
458
+ id: vecId,
459
+ ...update.vector ? { vector: update.vector } : {},
460
+ ...update.metadata || {}
461
+ }));
462
+ const batchSize = 1e3;
463
+ for (let i = 0; i < records.length; i += batchSize) {
464
+ const batch = records.slice(i, i + batchSize);
465
+ await namespace.write({
466
+ upsert_rows: batch,
467
+ distance_metric: distanceMetric
468
+ });
469
+ }
470
+ } catch (error) {
471
+ if (error instanceof _mastra_core_error.MastraError) throw error;
472
+ throw new _mastra_core_error.MastraError({
473
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "UPDATE_VECTOR", "FAILED"),
474
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
475
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
476
+ details: {
477
+ indexName,
478
+ ...id && { id },
479
+ ...filter && { filter: JSON.stringify(filter) }
480
+ }
481
+ }, error);
482
+ }
483
+ }
484
+ /**
485
+ * Deletes a vector by its ID.
486
+ * @param indexName - The name of the index containing the vector.
487
+ * @param id - The ID of the vector to delete.
488
+ * @returns A promise that resolves when the deletion is complete.
489
+ * @throws Will throw an error if the deletion operation fails.
490
+ */
491
+ async deleteVector({ indexName, id }) {
492
+ try {
493
+ await this.client.namespace(indexName).write({ deletes: [id] });
494
+ } catch (error) {
495
+ throw new _mastra_core_error.MastraError({
496
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "DELETE_VECTOR", "FAILED"),
497
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
498
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
499
+ details: { indexName }
500
+ }, error);
501
+ }
502
+ }
503
+ async deleteVectors({ indexName, filter, ids }) {
504
+ if (ids && filter) throw new _mastra_core_error.MastraError({
505
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
506
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
507
+ category: _mastra_core_error.ErrorCategory.USER,
508
+ text: "ids and filter are mutually exclusive",
509
+ details: { indexName }
510
+ });
511
+ if (!ids && !filter) throw new _mastra_core_error.MastraError({
512
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "DELETE_VECTORS", "NO_TARGET"),
513
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
514
+ category: _mastra_core_error.ErrorCategory.USER,
515
+ text: "Either filter or ids must be provided",
516
+ details: { indexName }
517
+ });
518
+ if (ids && ids.length === 0) throw new _mastra_core_error.MastraError({
519
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "DELETE_VECTORS", "EMPTY_IDS"),
520
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
521
+ category: _mastra_core_error.ErrorCategory.USER,
522
+ text: "ids array cannot be empty",
523
+ details: { indexName }
524
+ });
525
+ if (filter && Object.keys(filter).length === 0) throw new _mastra_core_error.MastraError({
526
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "DELETE_VECTORS", "EMPTY_FILTER"),
527
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
528
+ category: _mastra_core_error.ErrorCategory.USER,
529
+ text: "Filter cannot be an empty object",
530
+ details: { indexName }
531
+ });
532
+ try {
533
+ const namespace = this.client.namespace(indexName);
534
+ let idsToDelete = [];
535
+ if (ids) idsToDelete = ids;
536
+ else if (filter) {
537
+ const createIndex = this.createIndexCache.get(indexName);
538
+ if (!createIndex) throw new Error(`createIndex() not called for this index`);
539
+ const dummyVector = new Array(createIndex.dimension).fill(1 / Math.sqrt(createIndex.dimension));
540
+ const translatedFilter = this.filterTranslator.translate(filter);
541
+ idsToDelete = ((await namespace.query({
542
+ rank_by: [
543
+ "vector",
544
+ "ANN",
545
+ dummyVector
546
+ ],
547
+ top_k: 1e4,
548
+ filters: translatedFilter,
549
+ include_attributes: []
550
+ })).rows ?? []).map((r) => String(r.id));
551
+ }
552
+ if (idsToDelete.length === 0) {
553
+ this.logger.info(`No vectors matched the criteria for deletion in ${indexName}`);
554
+ return;
555
+ }
556
+ const batchSize = 1e3;
557
+ for (let i = 0; i < idsToDelete.length; i += batchSize) {
558
+ const batch = idsToDelete.slice(i, i + batchSize);
559
+ await namespace.write({ deletes: batch });
560
+ }
561
+ } catch (error) {
562
+ if (error instanceof _mastra_core_error.MastraError) throw error;
563
+ throw new _mastra_core_error.MastraError({
564
+ id: (0, _mastra_core_storage.createVectorErrorId)("TURBOPUFFER", "DELETE_VECTORS", "FAILED"),
565
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
566
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
567
+ details: {
568
+ indexName,
569
+ ...filter && { filter: JSON.stringify(filter) },
570
+ ...ids && { idsCount: ids.length }
571
+ }
572
+ }, error);
573
+ }
574
+ }
681
575
  };
682
-
576
+ //#endregion
683
577
  exports.TurbopufferVector = TurbopufferVector;
684
- //# sourceMappingURL=index.cjs.map
578
+
685
579
  //# sourceMappingURL=index.cjs.map