@mastra/s3vectors 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/docs/SKILL.md +3 -6
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/{docs-rag-retrieval.md → reference-rag-retrieval.md} +38 -19
- package/dist/docs/references/{docs-rag-vector-databases.md → reference-rag-vector-databases.md} +70 -5
- package/dist/docs/references/reference-vectors-s3vectors.md +10 -8
- package/dist/index.cjs +648 -699
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +646 -696
- package/dist/index.js.map +1 -1
- package/package.json +17 -16
package/dist/index.cjs
CHANGED
|
@@ -1,710 +1,659 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Validates and normalizes a primitive used in field equality (implicit `$eq`).
|
|
153
|
-
* @param value - Candidate equality value.
|
|
154
|
-
* @throws If the value is a `Date` or not a supported primitive.
|
|
155
|
-
*/
|
|
156
|
-
validateAndNormalizePrimitive(value) {
|
|
157
|
-
if (value instanceof Date) {
|
|
158
|
-
throw new Error("Invalid equality value. Only string, number, or boolean are supported by S3 Vectors");
|
|
159
|
-
}
|
|
160
|
-
return this.toPrimitiveForS3(value, "equality");
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Determines whether a filter is considered empty.
|
|
164
|
-
* @param filter - Input filter.
|
|
165
|
-
*/
|
|
166
|
-
isEmpty(filter) {
|
|
167
|
-
return filter === void 0 || filter === null || typeof filter === "object" && Object.keys(filter).length === 0;
|
|
168
|
-
}
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _aws_sdk_client_s3vectors = require("@aws-sdk/client-s3vectors");
|
|
3
|
+
let _lukeed_uuid = require("@lukeed/uuid");
|
|
4
|
+
let _mastra_core_error = require("@mastra/core/error");
|
|
5
|
+
let _mastra_core_storage = require("@mastra/core/storage");
|
|
6
|
+
let _mastra_core_vector = require("@mastra/core/vector");
|
|
7
|
+
let _mastra_core_vector_filter = require("@mastra/core/vector/filter");
|
|
8
|
+
//#region src/vector/filter.ts
|
|
9
|
+
/**
|
|
10
|
+
* Translates a high-level filter into the S3 Vectors filter shape.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* - Canonicalizes **implicit AND** (e.g. `{a:1,b:2}`) into explicit `{$and:[{a:1},{b:2}]}` in any
|
|
14
|
+
* non-field context that lacks `$and/$or`.
|
|
15
|
+
* - Normalizes `Date` values to epoch milliseconds where allowed (numeric comparisons and array elements).
|
|
16
|
+
* - Disallows `Date` at equality positions (including implicit equality).
|
|
17
|
+
* - Validates shapes using the base class after translation.
|
|
18
|
+
*/
|
|
19
|
+
var S3VectorsFilterTranslator = class extends _mastra_core_vector_filter.BaseFilterTranslator {
|
|
20
|
+
/** @inheritdoc */
|
|
21
|
+
getSupportedOperators() {
|
|
22
|
+
return {
|
|
23
|
+
logical: ["$and", "$or"],
|
|
24
|
+
basic: ["$eq", "$ne"],
|
|
25
|
+
numeric: [
|
|
26
|
+
"$gt",
|
|
27
|
+
"$gte",
|
|
28
|
+
"$lt",
|
|
29
|
+
"$lte"
|
|
30
|
+
],
|
|
31
|
+
array: ["$in", "$nin"],
|
|
32
|
+
element: ["$exists"]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Translates and validates a filter.
|
|
37
|
+
* @param filter - Input filter; may be `undefined`, `null`, or `{}` (all treated as empty).
|
|
38
|
+
* @returns The translated filter (or the original value if empty).
|
|
39
|
+
*/
|
|
40
|
+
translate(filter) {
|
|
41
|
+
if (this.isEmpty(filter)) return filter;
|
|
42
|
+
const translated = this.translateNode(filter, false);
|
|
43
|
+
this.validateFilter(translated);
|
|
44
|
+
return translated;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Recursively translates a node.
|
|
48
|
+
* @param node - Current node to translate.
|
|
49
|
+
* @param inFieldValue - When `true`, the node is the value of a field (i.e., equality context).
|
|
50
|
+
* @remarks
|
|
51
|
+
* - In a **field-value** context, only primitives or operator objects are allowed.
|
|
52
|
+
* - In a **non-field** context (root / logical branches), operator keys are processed;
|
|
53
|
+
* plain keys become field equalities and are validated.
|
|
54
|
+
* - Implicit AND is canonicalized in non-field contexts when multiple non-logical keys exist.
|
|
55
|
+
*/
|
|
56
|
+
translateNode(node, inFieldValue = false) {
|
|
57
|
+
if (this.isPrimitive(node) || node instanceof Date) return inFieldValue ? this.validateAndNormalizePrimitive(node) : node;
|
|
58
|
+
if (Array.isArray(node)) {
|
|
59
|
+
if (inFieldValue) throw new Error("Array equality is not supported in S3 Vectors. Use $in / $nin operators.");
|
|
60
|
+
return node;
|
|
61
|
+
}
|
|
62
|
+
const entries = Object.entries(node);
|
|
63
|
+
if (inFieldValue) {
|
|
64
|
+
if (entries.length === 0) throw new Error("Invalid equality value. Only string, number, or boolean are supported by S3 Vectors");
|
|
65
|
+
if (!entries.every(([k]) => this.isOperator(k))) throw new Error("Invalid equality value. Only string, number, or boolean are supported by S3 Vectors");
|
|
66
|
+
const opEntries = entries.map(([key, value]) => [key, this.translateOperatorValue(key, value)]);
|
|
67
|
+
return Object.fromEntries(opEntries);
|
|
68
|
+
}
|
|
69
|
+
const translatedEntries = entries.map(([key, value]) => {
|
|
70
|
+
if (this.isOperator(key)) return [key, this.translateOperatorValue(key, value)];
|
|
71
|
+
return [key, this.translateNode(value, true)];
|
|
72
|
+
});
|
|
73
|
+
const obj = Object.fromEntries(translatedEntries);
|
|
74
|
+
const keys = Object.keys(obj);
|
|
75
|
+
if (!keys.some((k) => k === "$and" || k === "$or")) {
|
|
76
|
+
const nonLogical = keys.filter((k) => k !== "$and" && k !== "$or");
|
|
77
|
+
if (nonLogical.length > 1) return { $and: nonLogical.map((k) => ({ [k]: obj[k] })) };
|
|
78
|
+
}
|
|
79
|
+
return obj;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Translates a single operator and validates its value.
|
|
83
|
+
* @param operator - One of the supported query operators.
|
|
84
|
+
* @param value - Operator value to normalize/validate.
|
|
85
|
+
*/
|
|
86
|
+
translateOperatorValue(operator, value) {
|
|
87
|
+
if (operator === "$and" || operator === "$or") {
|
|
88
|
+
if (!Array.isArray(value) || value.length === 0) throw new Error(`Value for logical operator ${operator} must be a non-empty array`);
|
|
89
|
+
return value.map((item) => this.translateNode(item));
|
|
90
|
+
}
|
|
91
|
+
if (operator === "$eq" || operator === "$ne") {
|
|
92
|
+
if (value instanceof Date) throw new Error("Invalid equality value. Only string, number, or boolean are supported by S3 Vectors");
|
|
93
|
+
return this.toPrimitiveForS3(value, operator);
|
|
94
|
+
}
|
|
95
|
+
if (operator === "$gt" || operator === "$gte" || operator === "$lt" || operator === "$lte") return this.toNumberForRange(value, operator);
|
|
96
|
+
if (operator === "$in" || operator === "$nin") {
|
|
97
|
+
if (!Array.isArray(value) || value.length === 0) throw new Error(`Value for array operator ${operator} must be a non-empty array`);
|
|
98
|
+
return value.map((v) => this.toPrimitiveForS3(v, operator));
|
|
99
|
+
}
|
|
100
|
+
if (operator === "$exists") {
|
|
101
|
+
if (typeof value !== "boolean") throw new Error(`Value for $exists operator must be a boolean`);
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
throw new Error(`Unsupported operator: ${operator}`);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Normalizes a value to an S3-accepted primitive.
|
|
108
|
+
* @param value - String | Number | Boolean | Date.
|
|
109
|
+
* @param operatorForMessage - Operator name used in error messages.
|
|
110
|
+
* @returns The normalized primitive; `Date` becomes epoch milliseconds.
|
|
111
|
+
* @throws If the value is not a supported primitive or is null/undefined.
|
|
112
|
+
*/
|
|
113
|
+
toPrimitiveForS3(value, operatorForMessage) {
|
|
114
|
+
if (value === null || value === void 0) {
|
|
115
|
+
if (operatorForMessage === "equality") throw new Error("S3 Vectors does not support null/undefined for equality");
|
|
116
|
+
throw new Error(`Value for ${operatorForMessage} must be string, number, or boolean`);
|
|
117
|
+
}
|
|
118
|
+
if (value instanceof Date) return value.getTime();
|
|
119
|
+
const t = typeof value;
|
|
120
|
+
if (t === "string" || t === "boolean") return value;
|
|
121
|
+
if (t === "number") return Object.is(value, -0) ? 0 : value;
|
|
122
|
+
throw new Error(`Value for ${operatorForMessage} must be string, number, or boolean`);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Ensures a numeric value for range operators; allows `Date` by converting to epoch ms.
|
|
126
|
+
* @param value - Candidate value.
|
|
127
|
+
* @param operatorForMessage - Operator name used in error messages.
|
|
128
|
+
* @throws If the value is not a number (or a Date).
|
|
129
|
+
*/
|
|
130
|
+
toNumberForRange(value, operatorForMessage) {
|
|
131
|
+
if (value instanceof Date) return value.getTime();
|
|
132
|
+
if (typeof value === "number" && !Number.isNaN(value)) return Object.is(value, -0) ? 0 : value;
|
|
133
|
+
throw new Error(`Value for ${operatorForMessage} must be a number`);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Validates and normalizes a primitive used in field equality (implicit `$eq`).
|
|
137
|
+
* @param value - Candidate equality value.
|
|
138
|
+
* @throws If the value is a `Date` or not a supported primitive.
|
|
139
|
+
*/
|
|
140
|
+
validateAndNormalizePrimitive(value) {
|
|
141
|
+
if (value instanceof Date) throw new Error("Invalid equality value. Only string, number, or boolean are supported by S3 Vectors");
|
|
142
|
+
return this.toPrimitiveForS3(value, "equality");
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Determines whether a filter is considered empty.
|
|
146
|
+
* @param filter - Input filter.
|
|
147
|
+
*/
|
|
148
|
+
isEmpty(filter) {
|
|
149
|
+
return filter === void 0 || filter === null || typeof filter === "object" && Object.keys(filter).length === 0;
|
|
150
|
+
}
|
|
169
151
|
};
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
let total = 0;
|
|
629
|
-
let nextToken;
|
|
630
|
-
do {
|
|
631
|
-
const out = await this.client.send(
|
|
632
|
-
new clientS3vectors.ListVectorsCommand({
|
|
633
|
-
...this.bucketParams(),
|
|
634
|
-
indexName,
|
|
635
|
-
maxResults: 1e3,
|
|
636
|
-
nextToken,
|
|
637
|
-
returnData: false,
|
|
638
|
-
returnMetadata: false
|
|
639
|
-
})
|
|
640
|
-
);
|
|
641
|
-
total += (out.vectors ?? []).length;
|
|
642
|
-
nextToken = out.nextToken;
|
|
643
|
-
} while (nextToken);
|
|
644
|
-
return total;
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Translates a high-level filter to the S3 Vectors filter shape.
|
|
648
|
-
* @internal
|
|
649
|
-
* @remarks Implicit AND is canonicalized by the translator where permitted by spec.
|
|
650
|
-
*/
|
|
651
|
-
transformFilter(filter) {
|
|
652
|
-
if (!filter) return void 0;
|
|
653
|
-
return this.filterTranslator.translate(filter);
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Converts a Mastra metric to an S3 metric.
|
|
657
|
-
* @internal
|
|
658
|
-
* @throws {Error} If the metric is not supported by S3 Vectors.
|
|
659
|
-
*/
|
|
660
|
-
static toS3Metric(metric) {
|
|
661
|
-
const m = _S3Vectors.METRIC_MAP[metric];
|
|
662
|
-
if (!m) {
|
|
663
|
-
throw new Error(`Invalid metric: "${metric}". S3 Vectors supports only: cosine, euclidean`);
|
|
664
|
-
}
|
|
665
|
-
return m;
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Monotonic transform from distance (smaller is better) to score (larger is better).
|
|
669
|
-
* @returns Number in (0, 1], preserving ranking.
|
|
670
|
-
*/
|
|
671
|
-
static distanceToScore(distance) {
|
|
672
|
-
return 1 / (1 + distance);
|
|
673
|
-
}
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/vector/index.ts
|
|
154
|
+
/**
|
|
155
|
+
* Vector store backed by Amazon S3 Vectors.
|
|
156
|
+
*
|
|
157
|
+
* @remarks
|
|
158
|
+
* - Supports `cosine` and `euclidean` distance metrics.
|
|
159
|
+
* - Filters must use explicit logical operators (`$and` / `$or`). The attached translator
|
|
160
|
+
* canonicalizes implicit AND (e.g., `{a:1,b:2}` → `{ $and: [{a:1},{b:2}] }`) where permitted by spec.
|
|
161
|
+
* - Methods wrap AWS errors in `MastraError` with domain/category metadata.
|
|
162
|
+
*/
|
|
163
|
+
var S3Vectors = class S3Vectors extends _mastra_core_vector.MastraVector {
|
|
164
|
+
client;
|
|
165
|
+
vectorBucketName;
|
|
166
|
+
nonFilterableMetadataKeys;
|
|
167
|
+
filterTranslator = new S3VectorsFilterTranslator();
|
|
168
|
+
static METRIC_MAP = {
|
|
169
|
+
cosine: "cosine",
|
|
170
|
+
euclidean: "euclidean"
|
|
171
|
+
};
|
|
172
|
+
constructor(opts) {
|
|
173
|
+
super({ id: opts.id });
|
|
174
|
+
if (!opts?.vectorBucketName) throw new _mastra_core_error.MastraError({
|
|
175
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "INITIALIZATION", "MISSING_BUCKET_NAME"),
|
|
176
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
177
|
+
category: _mastra_core_error.ErrorCategory.USER
|
|
178
|
+
}, /* @__PURE__ */ new Error("vectorBucketName is required"));
|
|
179
|
+
this.vectorBucketName = opts.vectorBucketName;
|
|
180
|
+
this.nonFilterableMetadataKeys = opts.nonFilterableMetadataKeys;
|
|
181
|
+
this.client = new _aws_sdk_client_s3vectors.S3VectorsClient({ ...opts.clientConfig ?? {} });
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* No-op to satisfy the base interface.
|
|
185
|
+
*
|
|
186
|
+
* @remarks The AWS SDK manages HTTP per request; no persistent connection is needed.
|
|
187
|
+
*/
|
|
188
|
+
async connect() {}
|
|
189
|
+
/**
|
|
190
|
+
* Closes the underlying AWS SDK HTTP handler to free sockets.
|
|
191
|
+
*/
|
|
192
|
+
async disconnect() {
|
|
193
|
+
try {
|
|
194
|
+
this.client.destroy();
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw new _mastra_core_error.MastraError({
|
|
197
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "DISCONNECT", "FAILED"),
|
|
198
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
199
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY
|
|
200
|
+
}, error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Creates an index or validates an existing one.
|
|
205
|
+
*
|
|
206
|
+
* @param params.indexName - Logical index name; normalized internally.
|
|
207
|
+
* @param params.dimension - Vector dimension (must be a positive integer).
|
|
208
|
+
* @param params.metric - Distance metric (`cosine` | `euclidean`). Defaults to `cosine`.
|
|
209
|
+
* @throws {MastraError} If arguments are invalid or AWS returns an error.
|
|
210
|
+
* @remarks
|
|
211
|
+
* On `ConflictException`, we verify the existing index schema via the parent implementation
|
|
212
|
+
* and return if it matches.
|
|
213
|
+
*/
|
|
214
|
+
async createIndex({ indexName, dimension, metric = "cosine" }) {
|
|
215
|
+
indexName = normalizeIndexName(indexName);
|
|
216
|
+
let s3Metric;
|
|
217
|
+
try {
|
|
218
|
+
assertPositiveInteger(dimension, "dimension");
|
|
219
|
+
s3Metric = S3Vectors.toS3Metric(metric);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
throw new _mastra_core_error.MastraError({
|
|
222
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "CREATE_INDEX", "INVALID_ARGS"),
|
|
223
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
224
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
225
|
+
details: {
|
|
226
|
+
indexName,
|
|
227
|
+
dimension,
|
|
228
|
+
metric
|
|
229
|
+
}
|
|
230
|
+
}, error);
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const input = {
|
|
234
|
+
...this.bucketParams(),
|
|
235
|
+
indexName,
|
|
236
|
+
dataType: "float32",
|
|
237
|
+
dimension,
|
|
238
|
+
distanceMetric: s3Metric
|
|
239
|
+
};
|
|
240
|
+
if (this.nonFilterableMetadataKeys?.length) input.metadataConfiguration = { nonFilterableMetadataKeys: this.nonFilterableMetadataKeys };
|
|
241
|
+
await this.client.send(new _aws_sdk_client_s3vectors.CreateIndexCommand(input));
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (error?.name === "ConflictException") {
|
|
244
|
+
await this.validateExistingIndex(indexName, dimension, metric);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
throw new _mastra_core_error.MastraError({
|
|
248
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "CREATE_INDEX", "FAILED"),
|
|
249
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
250
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
251
|
+
details: {
|
|
252
|
+
indexName,
|
|
253
|
+
dimension,
|
|
254
|
+
metric
|
|
255
|
+
}
|
|
256
|
+
}, error);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Upserts vectors in bulk.
|
|
261
|
+
*
|
|
262
|
+
* @param params.indexName - Index to write to.
|
|
263
|
+
* @param params.vectors - Array of vectors; each must match the index dimension.
|
|
264
|
+
* @param params.metadata - Optional metadata per vector; `Date` values are normalized to epoch ms.
|
|
265
|
+
* @param params.ids - Optional explicit IDs; if omitted, UUIDs are generated.
|
|
266
|
+
* @returns Array of IDs used for the upsert (explicit or generated).
|
|
267
|
+
* @throws {MastraError} If validation fails or AWS returns an error.
|
|
268
|
+
*/
|
|
269
|
+
async upsert({ indexName, vectors, metadata, ids }) {
|
|
270
|
+
indexName = normalizeIndexName(indexName);
|
|
271
|
+
try {
|
|
272
|
+
const { dimension } = await this.getIndexInfo(indexName);
|
|
273
|
+
validateVectorDimensions(vectors, dimension);
|
|
274
|
+
const generatedIds = ids ?? vectors.map(() => (0, _lukeed_uuid.v4)());
|
|
275
|
+
const putInput = {
|
|
276
|
+
...this.bucketParams(),
|
|
277
|
+
indexName,
|
|
278
|
+
vectors: vectors.map((vec, i) => ({
|
|
279
|
+
key: generatedIds[i],
|
|
280
|
+
data: { float32: vec },
|
|
281
|
+
metadata: normalizeMetadata(metadata?.[i])
|
|
282
|
+
}))
|
|
283
|
+
};
|
|
284
|
+
await this.client.send(new _aws_sdk_client_s3vectors.PutVectorsCommand(putInput));
|
|
285
|
+
return generatedIds;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
throw new _mastra_core_error.MastraError({
|
|
288
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "UPSERT", "FAILED"),
|
|
289
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
290
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
291
|
+
details: { indexName }
|
|
292
|
+
}, error);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Queries nearest neighbors.
|
|
297
|
+
*
|
|
298
|
+
* @param params.indexName - Target index.
|
|
299
|
+
* @param params.queryVector - Query vector (non-empty float32 array).
|
|
300
|
+
* @param params.topK - Number of neighbors to return (positive integer). Defaults to 10.
|
|
301
|
+
* @param params.filter - Metadata filter using explicit `$and`/`$or` (translator canonicalizes implicit AND).
|
|
302
|
+
* @param params.includeVector - If `true`, fetches missing vector data in a second call.
|
|
303
|
+
* @returns Results sorted by `score` descending.
|
|
304
|
+
* @throws {MastraError} If validation fails or AWS returns an error.
|
|
305
|
+
* @remarks
|
|
306
|
+
* `score = 1/(1+distance)` (monotonic transform), so ranking matches the underlying distance.
|
|
307
|
+
*/
|
|
308
|
+
async query({ indexName, queryVector, topK = 10, filter, includeVector = false }) {
|
|
309
|
+
indexName = normalizeIndexName(indexName);
|
|
310
|
+
if (!queryVector) throw new _mastra_core_error.MastraError({
|
|
311
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "QUERY", "MISSING_VECTOR"),
|
|
312
|
+
text: "queryVector is required for S3 Vectors queries. Metadata-only queries are not supported by this vector store.",
|
|
313
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
314
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
315
|
+
details: { indexName }
|
|
316
|
+
});
|
|
317
|
+
try {
|
|
318
|
+
if (!Array.isArray(queryVector) || queryVector.length === 0) throw new Error("queryVector must be a non-empty float32 array");
|
|
319
|
+
assertPositiveInteger(topK, "topK");
|
|
320
|
+
const translated = this.transformFilter(filter);
|
|
321
|
+
const vectors = ((await this.client.send(new _aws_sdk_client_s3vectors.QueryVectorsCommand({
|
|
322
|
+
...this.bucketParams(),
|
|
323
|
+
indexName,
|
|
324
|
+
topK,
|
|
325
|
+
queryVector: { float32: queryVector },
|
|
326
|
+
filter: translated && Object.keys(translated).length > 0 ? translated : void 0,
|
|
327
|
+
returnMetadata: true,
|
|
328
|
+
returnDistance: true
|
|
329
|
+
}))).vectors ?? []).filter((v) => !!v?.key);
|
|
330
|
+
let dataMap;
|
|
331
|
+
if (includeVector) {
|
|
332
|
+
const keys = vectors.filter((v) => v.key).map((v) => v.key);
|
|
333
|
+
if (keys.length > 0) {
|
|
334
|
+
const got = await this.client.send(new _aws_sdk_client_s3vectors.GetVectorsCommand({
|
|
335
|
+
...this.bucketParams(),
|
|
336
|
+
indexName,
|
|
337
|
+
keys,
|
|
338
|
+
returnData: true,
|
|
339
|
+
returnMetadata: false
|
|
340
|
+
}));
|
|
341
|
+
dataMap = {};
|
|
342
|
+
for (const g of got.vectors ?? []) if (g.key) dataMap[g.key] = g.data?.float32;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return vectors.map((v) => {
|
|
346
|
+
const id = v.key;
|
|
347
|
+
const result = {
|
|
348
|
+
id,
|
|
349
|
+
score: S3Vectors.distanceToScore(v.distance ?? 0)
|
|
350
|
+
};
|
|
351
|
+
const md = v.metadata;
|
|
352
|
+
if (md !== void 0) result.metadata = md;
|
|
353
|
+
if (includeVector) {
|
|
354
|
+
const vec = dataMap?.[id];
|
|
355
|
+
if (vec !== void 0) result.vector = vec;
|
|
356
|
+
}
|
|
357
|
+
return result;
|
|
358
|
+
});
|
|
359
|
+
} catch (error) {
|
|
360
|
+
throw new _mastra_core_error.MastraError({
|
|
361
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "QUERY", "FAILED"),
|
|
362
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
363
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
364
|
+
details: { indexName }
|
|
365
|
+
}, error);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Lists indexes within the configured bucket.
|
|
370
|
+
*
|
|
371
|
+
* @returns Array of index names.
|
|
372
|
+
* @throws {MastraError} On AWS errors.
|
|
373
|
+
*/
|
|
374
|
+
async listIndexes() {
|
|
375
|
+
try {
|
|
376
|
+
const names = [];
|
|
377
|
+
let nextToken;
|
|
378
|
+
do {
|
|
379
|
+
const out = await this.client.send(new _aws_sdk_client_s3vectors.ListIndexesCommand({
|
|
380
|
+
...this.bucketParams(),
|
|
381
|
+
nextToken
|
|
382
|
+
}));
|
|
383
|
+
for (const idx of out.indexes ?? []) if (idx.indexName) names.push(idx.indexName);
|
|
384
|
+
nextToken = out.nextToken;
|
|
385
|
+
} while (nextToken);
|
|
386
|
+
return names;
|
|
387
|
+
} catch (error) {
|
|
388
|
+
throw new _mastra_core_error.MastraError({
|
|
389
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "LIST_INDEXES", "FAILED"),
|
|
390
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
391
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY
|
|
392
|
+
}, error);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Returns index attributes.
|
|
397
|
+
*
|
|
398
|
+
* @param params.indexName - Index name.
|
|
399
|
+
* @returns Object containing `dimension`, `metric`, and `count`.
|
|
400
|
+
* @throws {MastraError} On AWS errors.
|
|
401
|
+
* @remarks
|
|
402
|
+
* `count` is computed via `ListVectors` pagination and may be costly (O(n)).
|
|
403
|
+
*/
|
|
404
|
+
async describeIndex({ indexName }) {
|
|
405
|
+
indexName = normalizeIndexName(indexName);
|
|
406
|
+
try {
|
|
407
|
+
const { dimension, metric } = await this.getIndexInfo(indexName);
|
|
408
|
+
return {
|
|
409
|
+
dimension,
|
|
410
|
+
metric,
|
|
411
|
+
count: await this.countVectors(indexName)
|
|
412
|
+
};
|
|
413
|
+
} catch (error) {
|
|
414
|
+
throw new _mastra_core_error.MastraError({
|
|
415
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "DESCRIBE_INDEX", "FAILED"),
|
|
416
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
417
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
418
|
+
details: { indexName }
|
|
419
|
+
}, error);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Deletes an index.
|
|
424
|
+
*
|
|
425
|
+
* @param params.indexName - Index name.
|
|
426
|
+
* @throws {MastraError} On AWS errors.
|
|
427
|
+
*/
|
|
428
|
+
async deleteIndex({ indexName }) {
|
|
429
|
+
indexName = normalizeIndexName(indexName);
|
|
430
|
+
try {
|
|
431
|
+
await this.client.send(new _aws_sdk_client_s3vectors.DeleteIndexCommand({
|
|
432
|
+
...this.bucketParams(),
|
|
433
|
+
indexName
|
|
434
|
+
}));
|
|
435
|
+
} catch (error) {
|
|
436
|
+
throw new _mastra_core_error.MastraError({
|
|
437
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "DELETE_INDEX", "FAILED"),
|
|
438
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
439
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
440
|
+
details: { indexName }
|
|
441
|
+
}, error);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Updates (replaces) a vector and/or its metadata by ID.
|
|
446
|
+
*
|
|
447
|
+
* @param params.indexName - Target index.
|
|
448
|
+
* @param params.id - Vector ID.
|
|
449
|
+
* @param params.update.vector - New vector; if omitted, the existing vector is reused.
|
|
450
|
+
* @param params.update.metadata - New metadata, merged with current metadata.
|
|
451
|
+
* @throws {MastraError} If the vector does not exist and `update.vector` is omitted, or on AWS error.
|
|
452
|
+
* @remarks
|
|
453
|
+
* S3 Vectors `PutVectors` is replace-all; we `Get` the current item, merge, then `Put`.
|
|
454
|
+
*/
|
|
455
|
+
async updateVector({ indexName, id, update }) {
|
|
456
|
+
if (!id) throw new _mastra_core_error.MastraError({
|
|
457
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "UPDATE_VECTOR", "INVALID_ARGS"),
|
|
458
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
459
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
460
|
+
text: "id is required for S3Vectors updateVector",
|
|
461
|
+
details: { indexName }
|
|
462
|
+
});
|
|
463
|
+
indexName = normalizeIndexName(indexName);
|
|
464
|
+
try {
|
|
465
|
+
if (!update.vector && !update.metadata) throw new Error("No updates provided");
|
|
466
|
+
const current = ((await this.client.send(new _aws_sdk_client_s3vectors.GetVectorsCommand({
|
|
467
|
+
...this.bucketParams(),
|
|
468
|
+
indexName,
|
|
469
|
+
keys: [id],
|
|
470
|
+
returnData: true,
|
|
471
|
+
returnMetadata: true
|
|
472
|
+
}))).vectors ?? [])[0];
|
|
473
|
+
const newVector = update.vector ?? current?.data?.float32;
|
|
474
|
+
if (!newVector) throw new Error(`Vector "${id}" not found. Provide update.vector to create it.`);
|
|
475
|
+
const newMetadata = update.metadata !== void 0 ? normalizeMetadata(update.metadata) : current?.metadata ?? {};
|
|
476
|
+
await this.client.send(new _aws_sdk_client_s3vectors.PutVectorsCommand({
|
|
477
|
+
...this.bucketParams(),
|
|
478
|
+
indexName,
|
|
479
|
+
vectors: [{
|
|
480
|
+
key: id,
|
|
481
|
+
data: { float32: newVector },
|
|
482
|
+
metadata: newMetadata
|
|
483
|
+
}]
|
|
484
|
+
}));
|
|
485
|
+
} catch (error) {
|
|
486
|
+
throw new _mastra_core_error.MastraError({
|
|
487
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "UPDATE_VECTOR", "FAILED"),
|
|
488
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
489
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
490
|
+
details: {
|
|
491
|
+
indexName,
|
|
492
|
+
...id && { id }
|
|
493
|
+
}
|
|
494
|
+
}, error);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Deletes a vector by ID.
|
|
499
|
+
*
|
|
500
|
+
* @param params.indexName - Target index.
|
|
501
|
+
* @param params.id - Vector ID to delete.
|
|
502
|
+
* @throws {MastraError} On AWS errors.
|
|
503
|
+
*/
|
|
504
|
+
async deleteVector({ indexName, id }) {
|
|
505
|
+
indexName = normalizeIndexName(indexName);
|
|
506
|
+
try {
|
|
507
|
+
await this.client.send(new _aws_sdk_client_s3vectors.DeleteVectorsCommand({
|
|
508
|
+
...this.bucketParams(),
|
|
509
|
+
indexName,
|
|
510
|
+
keys: [id]
|
|
511
|
+
}));
|
|
512
|
+
} catch (error) {
|
|
513
|
+
throw new _mastra_core_error.MastraError({
|
|
514
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "DELETE_VECTOR", "FAILED"),
|
|
515
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
516
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
517
|
+
details: {
|
|
518
|
+
indexName,
|
|
519
|
+
...id && { id }
|
|
520
|
+
}
|
|
521
|
+
}, error);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
async deleteVectors({ indexName, filter, ids }) {
|
|
525
|
+
throw new _mastra_core_error.MastraError({
|
|
526
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("S3VECTORS", "DELETE_VECTORS", "NOT_SUPPORTED"),
|
|
527
|
+
text: "deleteVectors is not yet implemented for S3Vectors vector store",
|
|
528
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
529
|
+
category: _mastra_core_error.ErrorCategory.SYSTEM,
|
|
530
|
+
details: {
|
|
531
|
+
indexName,
|
|
532
|
+
...filter && { filter: JSON.stringify(filter) },
|
|
533
|
+
...ids && { idsCount: ids.length }
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Returns shared bucket parameters for AWS SDK calls.
|
|
539
|
+
* @internal
|
|
540
|
+
*/
|
|
541
|
+
bucketParams() {
|
|
542
|
+
return { vectorBucketName: this.vectorBucketName };
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Retrieves index dimension/metric via `GetIndex`.
|
|
546
|
+
* @internal
|
|
547
|
+
* @throws {Error} If the index does not exist.
|
|
548
|
+
* @returns `{ dimension, metric }`, where `metric` includes `'dotproduct'` to satisfy Mastra types (S3 never returns it).
|
|
549
|
+
*/
|
|
550
|
+
async getIndexInfo(indexName) {
|
|
551
|
+
const idx = (await this.client.send(new _aws_sdk_client_s3vectors.GetIndexCommand({
|
|
552
|
+
...this.bucketParams(),
|
|
553
|
+
indexName
|
|
554
|
+
}))).index;
|
|
555
|
+
if (!idx) throw new Error(`Index "${indexName}" not found`);
|
|
556
|
+
const metric = idx.distanceMetric ?? "cosine";
|
|
557
|
+
return {
|
|
558
|
+
dimension: idx.dimension ?? 0,
|
|
559
|
+
metric
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Pages through `ListVectors` and counts total items.
|
|
564
|
+
* @internal
|
|
565
|
+
* @remarks O(n). Avoid calling on hot paths.
|
|
566
|
+
*/
|
|
567
|
+
async countVectors(indexName) {
|
|
568
|
+
let total = 0;
|
|
569
|
+
let nextToken;
|
|
570
|
+
do {
|
|
571
|
+
const out = await this.client.send(new _aws_sdk_client_s3vectors.ListVectorsCommand({
|
|
572
|
+
...this.bucketParams(),
|
|
573
|
+
indexName,
|
|
574
|
+
maxResults: 1e3,
|
|
575
|
+
nextToken,
|
|
576
|
+
returnData: false,
|
|
577
|
+
returnMetadata: false
|
|
578
|
+
}));
|
|
579
|
+
total += (out.vectors ?? []).length;
|
|
580
|
+
nextToken = out.nextToken;
|
|
581
|
+
} while (nextToken);
|
|
582
|
+
return total;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Translates a high-level filter to the S3 Vectors filter shape.
|
|
586
|
+
* @internal
|
|
587
|
+
* @remarks Implicit AND is canonicalized by the translator where permitted by spec.
|
|
588
|
+
*/
|
|
589
|
+
transformFilter(filter) {
|
|
590
|
+
if (!filter) return void 0;
|
|
591
|
+
return this.filterTranslator.translate(filter);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Converts a Mastra metric to an S3 metric.
|
|
595
|
+
* @internal
|
|
596
|
+
* @throws {Error} If the metric is not supported by S3 Vectors.
|
|
597
|
+
*/
|
|
598
|
+
static toS3Metric(metric) {
|
|
599
|
+
const m = S3Vectors.METRIC_MAP[metric];
|
|
600
|
+
if (!m) throw new Error(`Invalid metric: "${metric}". S3 Vectors supports only: cosine, euclidean`);
|
|
601
|
+
return m;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Monotonic transform from distance (smaller is better) to score (larger is better).
|
|
605
|
+
* @returns Number in (0, 1], preserving ranking.
|
|
606
|
+
*/
|
|
607
|
+
static distanceToScore(distance) {
|
|
608
|
+
return 1 / (1 + distance);
|
|
609
|
+
}
|
|
674
610
|
};
|
|
611
|
+
/**
|
|
612
|
+
* Ensures a value is a positive integer.
|
|
613
|
+
* @throws {Error} If the value is not a positive integer.
|
|
614
|
+
* @internal
|
|
615
|
+
*/
|
|
675
616
|
function assertPositiveInteger(value, name) {
|
|
676
|
-
|
|
677
|
-
throw new Error(`${name} must be a positive integer`);
|
|
678
|
-
}
|
|
617
|
+
if (!Number.isInteger(value) || value <= 0) throw new Error(`${name} must be a positive integer`);
|
|
679
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* Validates that all vectors match the required dimension.
|
|
621
|
+
* @throws {Error} If any vector length differs from `dimension`.
|
|
622
|
+
* @internal
|
|
623
|
+
*/
|
|
680
624
|
function validateVectorDimensions(vectors, dimension) {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
if (len !== dimension) {
|
|
687
|
-
throw new Error(`Vector at index ${i} has invalid dimension ${len}. Expected ${dimension} dimensions.`);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
625
|
+
if (!Array.isArray(vectors) || vectors.length === 0) throw new Error("No vectors provided for validation");
|
|
626
|
+
for (let i = 0; i < vectors.length; i++) {
|
|
627
|
+
const len = vectors[i]?.length;
|
|
628
|
+
if (len !== dimension) throw new Error(`Vector at index ${i} has invalid dimension ${len}. Expected ${dimension} dimensions.`);
|
|
629
|
+
}
|
|
690
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* Normalizes metadata values for S3 Vectors: `Date` → epoch ms.
|
|
633
|
+
* @internal
|
|
634
|
+
*/
|
|
691
635
|
function normalizeMetadata(meta) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
}
|
|
697
|
-
return out;
|
|
636
|
+
if (!meta) return {};
|
|
637
|
+
const out = {};
|
|
638
|
+
for (const [k, v] of Object.entries(meta)) out[k] = v instanceof Date ? v.getTime() : v;
|
|
639
|
+
return out;
|
|
698
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Normalizes an index name to this store's canonical form (underscore → hyphen, lowercase).
|
|
643
|
+
* @internal
|
|
644
|
+
* @throws {TypeError} If the provided name is not a string.
|
|
645
|
+
*/
|
|
699
646
|
function normalizeIndexName(str) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}
|
|
703
|
-
return str.replace(/_/g, "-").toLowerCase();
|
|
647
|
+
if (typeof str !== "string") throw new TypeError("Index name must be a string");
|
|
648
|
+
return str.replace(/_/g, "-").toLowerCase();
|
|
704
649
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
650
|
+
//#endregion
|
|
651
|
+
//#region src/vector/prompt.ts
|
|
652
|
+
/**
|
|
653
|
+
* Vector store specific prompt that details supported operators and examples.
|
|
654
|
+
* This prompt helps users construct valid filters for Amazon S3 Vectors.
|
|
655
|
+
*/
|
|
656
|
+
const S3VECTORS_PROMPT = `When querying Amazon S3 Vectors, you can ONLY use the operators listed below. Any other operators will be rejected.
|
|
708
657
|
Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
|
|
709
658
|
If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
|
|
710
659
|
|
|
@@ -781,8 +730,8 @@ Example Complex Query:
|
|
|
781
730
|
] }
|
|
782
731
|
]
|
|
783
732
|
}`;
|
|
784
|
-
|
|
733
|
+
//#endregion
|
|
785
734
|
exports.S3VECTORS_PROMPT = S3VECTORS_PROMPT;
|
|
786
735
|
exports.S3Vectors = S3Vectors;
|
|
787
|
-
|
|
736
|
+
|
|
788
737
|
//# sourceMappingURL=index.cjs.map
|