@mastra/dynamodb 0.0.0-pass-headers-for-create-mastra-client-20250529200245 → 0.0.0-support-d1-client-20250701191943
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/_tsup-dts-rollup.d.cts +1160 -0
- package/dist/_tsup-dts-rollup.d.ts +1160 -0
- package/dist/index.cjs +379 -105
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +355 -81
- package/package.json +31 -17
- package/src/storage/index.test.ts +314 -1
- package/src/storage/index.ts +422 -98
package/src/storage/index.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
|
|
2
2
|
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
3
|
-
import type {
|
|
4
|
-
import type { MastraMessageV2 } from '@mastra/core/agent';
|
|
3
|
+
import type { MastraMessageContentV2 } from '@mastra/core/agent';
|
|
5
4
|
import { MessageList } from '@mastra/core/agent';
|
|
5
|
+
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
6
|
+
import type { StorageThreadType, MastraMessageV2, MastraMessageV1 } from '@mastra/core/memory';
|
|
7
|
+
|
|
6
8
|
import {
|
|
7
9
|
MastraStorage,
|
|
8
10
|
TABLE_THREADS,
|
|
@@ -11,7 +13,19 @@ import {
|
|
|
11
13
|
TABLE_EVALS,
|
|
12
14
|
TABLE_TRACES,
|
|
13
15
|
} from '@mastra/core/storage';
|
|
14
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
EvalRow,
|
|
18
|
+
StorageGetMessagesArg,
|
|
19
|
+
WorkflowRun,
|
|
20
|
+
WorkflowRuns,
|
|
21
|
+
TABLE_NAMES,
|
|
22
|
+
StorageGetTracesArg,
|
|
23
|
+
PaginationInfo,
|
|
24
|
+
StorageColumn,
|
|
25
|
+
TABLE_RESOURCES,
|
|
26
|
+
} from '@mastra/core/storage';
|
|
27
|
+
import type { Trace } from '@mastra/core/telemetry';
|
|
28
|
+
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
15
29
|
import type { Service } from 'electrodb';
|
|
16
30
|
import { getElectroDbService } from '../entities';
|
|
17
31
|
|
|
@@ -25,6 +39,8 @@ export interface DynamoDBStoreConfig {
|
|
|
25
39
|
};
|
|
26
40
|
}
|
|
27
41
|
|
|
42
|
+
type SUPPORTED_TABLE_NAMES = Exclude<TABLE_NAMES, typeof TABLE_RESOURCES>;
|
|
43
|
+
|
|
28
44
|
// Define a type for our service that allows string indexing
|
|
29
45
|
type MastraService = Service<Record<string, any>> & {
|
|
30
46
|
[key: string]: any;
|
|
@@ -51,25 +67,36 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
51
67
|
super({ name });
|
|
52
68
|
|
|
53
69
|
// Validate required config
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
try {
|
|
71
|
+
if (!config.tableName || typeof config.tableName !== 'string' || config.tableName.trim() === '') {
|
|
72
|
+
throw new Error('DynamoDBStore: config.tableName must be provided and cannot be empty.');
|
|
73
|
+
}
|
|
74
|
+
// Validate tableName characters (basic check)
|
|
75
|
+
if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
63
80
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
const dynamoClient = new DynamoDBClient({
|
|
82
|
+
region: config.region || 'us-east-1',
|
|
83
|
+
endpoint: config.endpoint,
|
|
84
|
+
credentials: config.credentials,
|
|
85
|
+
});
|
|
69
86
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
87
|
+
this.tableName = config.tableName;
|
|
88
|
+
this.client = DynamoDBDocumentClient.from(dynamoClient);
|
|
89
|
+
this.service = getElectroDbService(this.client, this.tableName) as MastraService;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw new MastraError(
|
|
92
|
+
{
|
|
93
|
+
id: 'STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED',
|
|
94
|
+
domain: ErrorDomain.STORAGE,
|
|
95
|
+
category: ErrorCategory.USER,
|
|
96
|
+
},
|
|
97
|
+
error,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
73
100
|
|
|
74
101
|
// We're using a single table design with ElectroDB,
|
|
75
102
|
// so we don't need to create multiple tables
|
|
@@ -101,7 +128,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
101
128
|
this.logger.debug(`Table ${this.tableName} exists and is accessible`);
|
|
102
129
|
} catch (error) {
|
|
103
130
|
this.logger.error('Error validating table access', { tableName: this.tableName, error });
|
|
104
|
-
throw
|
|
131
|
+
throw new MastraError(
|
|
132
|
+
{
|
|
133
|
+
id: 'STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED',
|
|
134
|
+
domain: ErrorDomain.STORAGE,
|
|
135
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
136
|
+
details: { tableName: this.tableName },
|
|
137
|
+
},
|
|
138
|
+
error,
|
|
139
|
+
);
|
|
105
140
|
}
|
|
106
141
|
}
|
|
107
142
|
|
|
@@ -127,7 +162,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
127
162
|
}
|
|
128
163
|
|
|
129
164
|
// For other errors (like permissions issues), we should throw
|
|
130
|
-
throw
|
|
165
|
+
throw new MastraError(
|
|
166
|
+
{
|
|
167
|
+
id: 'STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED',
|
|
168
|
+
domain: ErrorDomain.STORAGE,
|
|
169
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
170
|
+
details: { tableName: this.tableName },
|
|
171
|
+
},
|
|
172
|
+
error,
|
|
173
|
+
);
|
|
131
174
|
}
|
|
132
175
|
}
|
|
133
176
|
|
|
@@ -154,7 +197,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
154
197
|
// The error has already been handled by _performInitializationAndStore
|
|
155
198
|
// (i.e., this.hasInitialized was reset). Re-throwing here ensures
|
|
156
199
|
// the caller of init() is aware of the failure.
|
|
157
|
-
throw
|
|
200
|
+
throw new MastraError(
|
|
201
|
+
{
|
|
202
|
+
id: 'STORAGE_DYNAMODB_STORE_INIT_FAILED',
|
|
203
|
+
domain: ErrorDomain.STORAGE,
|
|
204
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
205
|
+
details: { tableName: this.tableName },
|
|
206
|
+
},
|
|
207
|
+
error,
|
|
208
|
+
);
|
|
158
209
|
}
|
|
159
210
|
}
|
|
160
211
|
|
|
@@ -181,15 +232,52 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
181
232
|
});
|
|
182
233
|
}
|
|
183
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Pre-processes a record to ensure Date objects are converted to ISO strings
|
|
237
|
+
* This is necessary because ElectroDB validation happens before setters are applied
|
|
238
|
+
*/
|
|
239
|
+
private preprocessRecord(record: Record<string, any>): Record<string, any> {
|
|
240
|
+
const processed = { ...record };
|
|
241
|
+
|
|
242
|
+
// Convert Date objects to ISO strings for date fields
|
|
243
|
+
// This prevents ElectroDB validation errors that occur when Date objects are passed
|
|
244
|
+
// to string-typed attributes, even when the attribute has a setter that converts dates
|
|
245
|
+
if (processed.createdAt instanceof Date) {
|
|
246
|
+
processed.createdAt = processed.createdAt.toISOString();
|
|
247
|
+
}
|
|
248
|
+
if (processed.updatedAt instanceof Date) {
|
|
249
|
+
processed.updatedAt = processed.updatedAt.toISOString();
|
|
250
|
+
}
|
|
251
|
+
if (processed.created_at instanceof Date) {
|
|
252
|
+
processed.created_at = processed.created_at.toISOString();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return processed;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async alterTable(_args: {
|
|
259
|
+
tableName: TABLE_NAMES;
|
|
260
|
+
schema: Record<string, StorageColumn>;
|
|
261
|
+
ifNotExists: string[];
|
|
262
|
+
}): Promise<void> {
|
|
263
|
+
// Nothing to do here, DynamoDB has a flexible schema and handles new attributes automatically upon insertion/update.
|
|
264
|
+
}
|
|
265
|
+
|
|
184
266
|
/**
|
|
185
267
|
* Clear all items from a logical "table" (entity type)
|
|
186
268
|
*/
|
|
187
|
-
async clearTable({ tableName }: { tableName:
|
|
269
|
+
async clearTable({ tableName }: { tableName: SUPPORTED_TABLE_NAMES }): Promise<void> {
|
|
188
270
|
this.logger.debug('DynamoDB clearTable called', { tableName });
|
|
189
271
|
|
|
190
272
|
const entityName = this.getEntityNameForTable(tableName);
|
|
191
273
|
if (!entityName || !this.service.entities[entityName]) {
|
|
192
|
-
throw new
|
|
274
|
+
throw new MastraError({
|
|
275
|
+
id: 'STORAGE_DYNAMODB_STORE_CLEAR_TABLE_INVALID_ARGS',
|
|
276
|
+
domain: ErrorDomain.STORAGE,
|
|
277
|
+
category: ErrorCategory.USER,
|
|
278
|
+
text: 'No entity defined for tableName',
|
|
279
|
+
details: { tableName },
|
|
280
|
+
});
|
|
193
281
|
}
|
|
194
282
|
|
|
195
283
|
try {
|
|
@@ -259,45 +347,83 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
259
347
|
|
|
260
348
|
this.logger.debug(`Successfully cleared all records for ${tableName}`);
|
|
261
349
|
} catch (error) {
|
|
262
|
-
|
|
263
|
-
|
|
350
|
+
throw new MastraError(
|
|
351
|
+
{
|
|
352
|
+
id: 'STORAGE_DYNAMODB_STORE_CLEAR_TABLE_FAILED',
|
|
353
|
+
domain: ErrorDomain.STORAGE,
|
|
354
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
355
|
+
details: { tableName },
|
|
356
|
+
},
|
|
357
|
+
error,
|
|
358
|
+
);
|
|
264
359
|
}
|
|
265
360
|
}
|
|
266
361
|
|
|
267
362
|
/**
|
|
268
363
|
* Insert a record into the specified "table" (entity)
|
|
269
364
|
*/
|
|
270
|
-
async insert({
|
|
365
|
+
async insert({
|
|
366
|
+
tableName,
|
|
367
|
+
record,
|
|
368
|
+
}: {
|
|
369
|
+
tableName: SUPPORTED_TABLE_NAMES;
|
|
370
|
+
record: Record<string, any>;
|
|
371
|
+
}): Promise<void> {
|
|
271
372
|
this.logger.debug('DynamoDB insert called', { tableName });
|
|
272
373
|
|
|
273
374
|
const entityName = this.getEntityNameForTable(tableName);
|
|
274
375
|
if (!entityName || !this.service.entities[entityName]) {
|
|
275
|
-
throw new
|
|
376
|
+
throw new MastraError({
|
|
377
|
+
id: 'STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS',
|
|
378
|
+
domain: ErrorDomain.STORAGE,
|
|
379
|
+
category: ErrorCategory.USER,
|
|
380
|
+
text: 'No entity defined for tableName',
|
|
381
|
+
details: { tableName },
|
|
382
|
+
});
|
|
276
383
|
}
|
|
277
384
|
|
|
278
385
|
try {
|
|
279
|
-
// Add the entity type to the record before creating
|
|
280
|
-
const dataToSave = { entity: entityName, ...record };
|
|
386
|
+
// Add the entity type to the record and preprocess before creating
|
|
387
|
+
const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
|
|
281
388
|
await this.service.entities[entityName].create(dataToSave).go();
|
|
282
389
|
} catch (error) {
|
|
283
|
-
|
|
284
|
-
|
|
390
|
+
throw new MastraError(
|
|
391
|
+
{
|
|
392
|
+
id: 'STORAGE_DYNAMODB_STORE_INSERT_FAILED',
|
|
393
|
+
domain: ErrorDomain.STORAGE,
|
|
394
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
395
|
+
details: { tableName },
|
|
396
|
+
},
|
|
397
|
+
error,
|
|
398
|
+
);
|
|
285
399
|
}
|
|
286
400
|
}
|
|
287
401
|
|
|
288
402
|
/**
|
|
289
403
|
* Insert multiple records as a batch
|
|
290
404
|
*/
|
|
291
|
-
async batchInsert({
|
|
405
|
+
async batchInsert({
|
|
406
|
+
tableName,
|
|
407
|
+
records,
|
|
408
|
+
}: {
|
|
409
|
+
tableName: SUPPORTED_TABLE_NAMES;
|
|
410
|
+
records: Record<string, any>[];
|
|
411
|
+
}): Promise<void> {
|
|
292
412
|
this.logger.debug('DynamoDB batchInsert called', { tableName, count: records.length });
|
|
293
413
|
|
|
294
414
|
const entityName = this.getEntityNameForTable(tableName);
|
|
295
415
|
if (!entityName || !this.service.entities[entityName]) {
|
|
296
|
-
throw new
|
|
416
|
+
throw new MastraError({
|
|
417
|
+
id: 'STORAGE_DYNAMODB_STORE_BATCH_INSERT_INVALID_ARGS',
|
|
418
|
+
domain: ErrorDomain.STORAGE,
|
|
419
|
+
category: ErrorCategory.USER,
|
|
420
|
+
text: 'No entity defined for tableName',
|
|
421
|
+
details: { tableName },
|
|
422
|
+
});
|
|
297
423
|
}
|
|
298
424
|
|
|
299
|
-
// Add entity type
|
|
300
|
-
const recordsToSave = records.map(rec => ({ entity: entityName, ...rec }));
|
|
425
|
+
// Add entity type and preprocess each record
|
|
426
|
+
const recordsToSave = records.map(rec => ({ entity: entityName, ...this.preprocessRecord(rec) }));
|
|
301
427
|
|
|
302
428
|
// ElectroDB has batch limits of 25 items, so we need to chunk
|
|
303
429
|
const batchSize = 25;
|
|
@@ -323,20 +449,39 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
323
449
|
// Original batch call: await this.service.entities[entityName].create(batch).go();
|
|
324
450
|
}
|
|
325
451
|
} catch (error) {
|
|
326
|
-
|
|
327
|
-
|
|
452
|
+
throw new MastraError(
|
|
453
|
+
{
|
|
454
|
+
id: 'STORAGE_DYNAMODB_STORE_BATCH_INSERT_FAILED',
|
|
455
|
+
domain: ErrorDomain.STORAGE,
|
|
456
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
457
|
+
details: { tableName },
|
|
458
|
+
},
|
|
459
|
+
error,
|
|
460
|
+
);
|
|
328
461
|
}
|
|
329
462
|
}
|
|
330
463
|
|
|
331
464
|
/**
|
|
332
465
|
* Load a record by its keys
|
|
333
466
|
*/
|
|
334
|
-
async load<R>({
|
|
467
|
+
async load<R>({
|
|
468
|
+
tableName,
|
|
469
|
+
keys,
|
|
470
|
+
}: {
|
|
471
|
+
tableName: SUPPORTED_TABLE_NAMES;
|
|
472
|
+
keys: Record<string, string>;
|
|
473
|
+
}): Promise<R | null> {
|
|
335
474
|
this.logger.debug('DynamoDB load called', { tableName, keys });
|
|
336
475
|
|
|
337
476
|
const entityName = this.getEntityNameForTable(tableName);
|
|
338
477
|
if (!entityName || !this.service.entities[entityName]) {
|
|
339
|
-
throw new
|
|
478
|
+
throw new MastraError({
|
|
479
|
+
id: 'STORAGE_DYNAMODB_STORE_LOAD_INVALID_ARGS',
|
|
480
|
+
domain: ErrorDomain.STORAGE,
|
|
481
|
+
category: ErrorCategory.USER,
|
|
482
|
+
text: 'No entity defined for tableName',
|
|
483
|
+
details: { tableName },
|
|
484
|
+
});
|
|
340
485
|
}
|
|
341
486
|
|
|
342
487
|
try {
|
|
@@ -361,8 +506,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
361
506
|
|
|
362
507
|
return data as R;
|
|
363
508
|
} catch (error) {
|
|
364
|
-
|
|
365
|
-
|
|
509
|
+
throw new MastraError(
|
|
510
|
+
{
|
|
511
|
+
id: 'STORAGE_DYNAMODB_STORE_LOAD_FAILED',
|
|
512
|
+
domain: ErrorDomain.STORAGE,
|
|
513
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
514
|
+
details: { tableName },
|
|
515
|
+
},
|
|
516
|
+
error,
|
|
517
|
+
);
|
|
366
518
|
}
|
|
367
519
|
}
|
|
368
520
|
|
|
@@ -380,12 +532,22 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
380
532
|
const data = result.data;
|
|
381
533
|
return {
|
|
382
534
|
...data,
|
|
535
|
+
// Convert date strings back to Date objects for consistency
|
|
536
|
+
createdAt: typeof data.createdAt === 'string' ? new Date(data.createdAt) : data.createdAt,
|
|
537
|
+
updatedAt: typeof data.updatedAt === 'string' ? new Date(data.updatedAt) : data.updatedAt,
|
|
383
538
|
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
384
539
|
// metadata is already transformed by the entity's getter
|
|
385
540
|
} as StorageThreadType;
|
|
386
541
|
} catch (error) {
|
|
387
|
-
|
|
388
|
-
|
|
542
|
+
throw new MastraError(
|
|
543
|
+
{
|
|
544
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED',
|
|
545
|
+
domain: ErrorDomain.STORAGE,
|
|
546
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
547
|
+
details: { threadId },
|
|
548
|
+
},
|
|
549
|
+
error,
|
|
550
|
+
);
|
|
389
551
|
}
|
|
390
552
|
}
|
|
391
553
|
|
|
@@ -401,12 +563,22 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
401
563
|
// ElectroDB handles the transformation with attribute getters
|
|
402
564
|
return result.data.map((data: any) => ({
|
|
403
565
|
...data,
|
|
566
|
+
// Convert date strings back to Date objects for consistency
|
|
567
|
+
createdAt: typeof data.createdAt === 'string' ? new Date(data.createdAt) : data.createdAt,
|
|
568
|
+
updatedAt: typeof data.updatedAt === 'string' ? new Date(data.updatedAt) : data.updatedAt,
|
|
404
569
|
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
405
570
|
// metadata is already transformed by the entity's getter
|
|
406
571
|
})) as StorageThreadType[];
|
|
407
572
|
} catch (error) {
|
|
408
|
-
|
|
409
|
-
|
|
573
|
+
throw new MastraError(
|
|
574
|
+
{
|
|
575
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED',
|
|
576
|
+
domain: ErrorDomain.STORAGE,
|
|
577
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
578
|
+
details: { resourceId },
|
|
579
|
+
},
|
|
580
|
+
error,
|
|
581
|
+
);
|
|
410
582
|
}
|
|
411
583
|
}
|
|
412
584
|
|
|
@@ -437,8 +609,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
437
609
|
metadata: thread.metadata,
|
|
438
610
|
};
|
|
439
611
|
} catch (error) {
|
|
440
|
-
|
|
441
|
-
|
|
612
|
+
throw new MastraError(
|
|
613
|
+
{
|
|
614
|
+
id: 'STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED',
|
|
615
|
+
domain: ErrorDomain.STORAGE,
|
|
616
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
617
|
+
details: { threadId: thread.id },
|
|
618
|
+
},
|
|
619
|
+
error,
|
|
620
|
+
);
|
|
442
621
|
}
|
|
443
622
|
}
|
|
444
623
|
|
|
@@ -493,8 +672,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
493
672
|
updatedAt: now,
|
|
494
673
|
};
|
|
495
674
|
} catch (error) {
|
|
496
|
-
|
|
497
|
-
|
|
675
|
+
throw new MastraError(
|
|
676
|
+
{
|
|
677
|
+
id: 'STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED',
|
|
678
|
+
domain: ErrorDomain.STORAGE,
|
|
679
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
680
|
+
details: { threadId: id },
|
|
681
|
+
},
|
|
682
|
+
error,
|
|
683
|
+
);
|
|
498
684
|
}
|
|
499
685
|
}
|
|
500
686
|
|
|
@@ -510,8 +696,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
510
696
|
// 2. Delete any vector embeddings related to this thread
|
|
511
697
|
// These would be additional operations
|
|
512
698
|
} catch (error) {
|
|
513
|
-
|
|
514
|
-
|
|
699
|
+
throw new MastraError(
|
|
700
|
+
{
|
|
701
|
+
id: 'STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED',
|
|
702
|
+
domain: ErrorDomain.STORAGE,
|
|
703
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
704
|
+
details: { threadId },
|
|
705
|
+
},
|
|
706
|
+
error,
|
|
707
|
+
);
|
|
515
708
|
}
|
|
516
709
|
}
|
|
517
710
|
|
|
@@ -531,18 +724,19 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
531
724
|
// Provide *all* composite key components for the 'byThread' index ('entity', 'threadId')
|
|
532
725
|
const query = this.service.entities.message.query.byThread({ entity: 'message', threadId });
|
|
533
726
|
|
|
727
|
+
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
534
728
|
// Apply the 'last' limit if provided
|
|
535
|
-
if (
|
|
536
|
-
// Use ElectroDB's limit parameter
|
|
537
|
-
//
|
|
538
|
-
//
|
|
539
|
-
const results = await query.go({ limit
|
|
729
|
+
if (limit !== Number.MAX_SAFE_INTEGER) {
|
|
730
|
+
// Use ElectroDB's limit parameter
|
|
731
|
+
// DDB GSIs are sorted in ascending order
|
|
732
|
+
// Use ElectroDB's order parameter to sort in descending order to retrieve 'latest' messages
|
|
733
|
+
const results = await query.go({ limit, order: 'desc' });
|
|
540
734
|
// Use arrow function in map to preserve 'this' context for parseMessageData
|
|
541
735
|
const list = new MessageList({ threadId, resourceId }).add(
|
|
542
736
|
results.data.map((data: any) => this.parseMessageData(data)),
|
|
543
737
|
'memory',
|
|
544
738
|
);
|
|
545
|
-
if (format === `v2`) return list.get.all.
|
|
739
|
+
if (format === `v2`) return list.get.all.v2();
|
|
546
740
|
return list.get.all.v1();
|
|
547
741
|
}
|
|
548
742
|
|
|
@@ -553,11 +747,18 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
553
747
|
results.data.map((data: any) => this.parseMessageData(data)),
|
|
554
748
|
'memory',
|
|
555
749
|
);
|
|
556
|
-
if (format === `v2`) return list.get.all.
|
|
750
|
+
if (format === `v2`) return list.get.all.v2();
|
|
557
751
|
return list.get.all.v1();
|
|
558
752
|
} catch (error) {
|
|
559
|
-
|
|
560
|
-
|
|
753
|
+
throw new MastraError(
|
|
754
|
+
{
|
|
755
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_MESSAGES_FAILED',
|
|
756
|
+
domain: ErrorDomain.STORAGE,
|
|
757
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
758
|
+
details: { threadId },
|
|
759
|
+
},
|
|
760
|
+
error,
|
|
761
|
+
);
|
|
561
762
|
}
|
|
562
763
|
}
|
|
563
764
|
async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
|
|
@@ -572,6 +773,11 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
572
773
|
return [];
|
|
573
774
|
}
|
|
574
775
|
|
|
776
|
+
const threadId = messages[0]?.threadId;
|
|
777
|
+
if (!threadId) {
|
|
778
|
+
throw new Error('Thread ID is required');
|
|
779
|
+
}
|
|
780
|
+
|
|
575
781
|
// Ensure 'entity' is added and complex fields are handled
|
|
576
782
|
const messagesToSave = messages.map(msg => {
|
|
577
783
|
const now = new Date().toISOString();
|
|
@@ -602,26 +808,41 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
602
808
|
batches.push(batch);
|
|
603
809
|
}
|
|
604
810
|
|
|
605
|
-
// Process each batch
|
|
606
|
-
|
|
607
|
-
//
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
811
|
+
// Process each batch and update thread's updatedAt in parallel for better performance
|
|
812
|
+
await Promise.all([
|
|
813
|
+
// Process message batches
|
|
814
|
+
...batches.map(async batch => {
|
|
815
|
+
for (const messageData of batch) {
|
|
816
|
+
// Ensure each item has the entity property before sending
|
|
817
|
+
if (!messageData.entity) {
|
|
818
|
+
this.logger.error('Missing entity property in message data for create', { messageData });
|
|
819
|
+
throw new Error('Internal error: Missing entity property during saveMessages');
|
|
820
|
+
}
|
|
821
|
+
await this.service.entities.message.put(messageData).go();
|
|
613
822
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
823
|
+
}),
|
|
824
|
+
// Update thread's updatedAt timestamp
|
|
825
|
+
this.service.entities.thread
|
|
826
|
+
.update({ entity: 'thread', id: threadId })
|
|
827
|
+
.set({
|
|
828
|
+
updatedAt: new Date().toISOString(),
|
|
829
|
+
})
|
|
830
|
+
.go(),
|
|
831
|
+
]);
|
|
618
832
|
|
|
619
833
|
const list = new MessageList().add(messages, 'memory');
|
|
620
834
|
if (format === `v1`) return list.get.all.v1();
|
|
621
|
-
return list.get.all.
|
|
835
|
+
return list.get.all.v2();
|
|
622
836
|
} catch (error) {
|
|
623
|
-
|
|
624
|
-
|
|
837
|
+
throw new MastraError(
|
|
838
|
+
{
|
|
839
|
+
id: 'STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED',
|
|
840
|
+
domain: ErrorDomain.STORAGE,
|
|
841
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
842
|
+
details: { count: messages.length },
|
|
843
|
+
},
|
|
844
|
+
error,
|
|
845
|
+
);
|
|
625
846
|
}
|
|
626
847
|
}
|
|
627
848
|
|
|
@@ -685,8 +906,14 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
685
906
|
|
|
686
907
|
return items;
|
|
687
908
|
} catch (error) {
|
|
688
|
-
|
|
689
|
-
|
|
909
|
+
throw new MastraError(
|
|
910
|
+
{
|
|
911
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_TRACES_FAILED',
|
|
912
|
+
domain: ErrorDomain.STORAGE,
|
|
913
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
914
|
+
},
|
|
915
|
+
error,
|
|
916
|
+
);
|
|
690
917
|
}
|
|
691
918
|
}
|
|
692
919
|
|
|
@@ -705,8 +932,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
705
932
|
records: recordsToSave, // Pass records with 'entity' included
|
|
706
933
|
});
|
|
707
934
|
} catch (error) {
|
|
708
|
-
|
|
709
|
-
|
|
935
|
+
throw new MastraError(
|
|
936
|
+
{
|
|
937
|
+
id: 'STORAGE_DYNAMODB_STORE_BATCH_TRACE_INSERT_FAILED',
|
|
938
|
+
domain: ErrorDomain.STORAGE,
|
|
939
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
940
|
+
details: { count: records.length },
|
|
941
|
+
},
|
|
942
|
+
error,
|
|
943
|
+
);
|
|
710
944
|
}
|
|
711
945
|
}
|
|
712
946
|
|
|
@@ -735,11 +969,18 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
735
969
|
updatedAt: now,
|
|
736
970
|
resourceId,
|
|
737
971
|
};
|
|
738
|
-
//
|
|
739
|
-
await this.service.entities.workflowSnapshot.
|
|
972
|
+
// Use upsert instead of create to handle both create and update cases
|
|
973
|
+
await this.service.entities.workflowSnapshot.upsert(data).go();
|
|
740
974
|
} catch (error) {
|
|
741
|
-
|
|
742
|
-
|
|
975
|
+
throw new MastraError(
|
|
976
|
+
{
|
|
977
|
+
id: 'STORAGE_DYNAMODB_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED',
|
|
978
|
+
domain: ErrorDomain.STORAGE,
|
|
979
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
980
|
+
details: { workflowName, runId },
|
|
981
|
+
},
|
|
982
|
+
error,
|
|
983
|
+
);
|
|
743
984
|
}
|
|
744
985
|
}
|
|
745
986
|
|
|
@@ -770,8 +1011,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
770
1011
|
// Parse the snapshot string
|
|
771
1012
|
return result.data.snapshot as WorkflowRunState;
|
|
772
1013
|
} catch (error) {
|
|
773
|
-
|
|
774
|
-
|
|
1014
|
+
throw new MastraError(
|
|
1015
|
+
{
|
|
1016
|
+
id: 'STORAGE_DYNAMODB_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED',
|
|
1017
|
+
domain: ErrorDomain.STORAGE,
|
|
1018
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1019
|
+
details: { workflowName, runId },
|
|
1020
|
+
},
|
|
1021
|
+
error,
|
|
1022
|
+
);
|
|
775
1023
|
}
|
|
776
1024
|
}
|
|
777
1025
|
|
|
@@ -861,8 +1109,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
861
1109
|
total,
|
|
862
1110
|
};
|
|
863
1111
|
} catch (error) {
|
|
864
|
-
|
|
865
|
-
|
|
1112
|
+
throw new MastraError(
|
|
1113
|
+
{
|
|
1114
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUNS_FAILED',
|
|
1115
|
+
domain: ErrorDomain.STORAGE,
|
|
1116
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1117
|
+
details: { workflowName: args?.workflowName || '', resourceId: args?.resourceId || '' },
|
|
1118
|
+
},
|
|
1119
|
+
error,
|
|
1120
|
+
);
|
|
866
1121
|
}
|
|
867
1122
|
}
|
|
868
1123
|
|
|
@@ -933,8 +1188,15 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
933
1188
|
resourceId: matchingRunDbItem.resourceId,
|
|
934
1189
|
};
|
|
935
1190
|
} catch (error) {
|
|
936
|
-
|
|
937
|
-
|
|
1191
|
+
throw new MastraError(
|
|
1192
|
+
{
|
|
1193
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED',
|
|
1194
|
+
domain: ErrorDomain.STORAGE,
|
|
1195
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1196
|
+
details: { runId, workflowName: args?.workflowName || '' },
|
|
1197
|
+
},
|
|
1198
|
+
error,
|
|
1199
|
+
);
|
|
938
1200
|
}
|
|
939
1201
|
}
|
|
940
1202
|
|
|
@@ -951,8 +1213,8 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
951
1213
|
}
|
|
952
1214
|
|
|
953
1215
|
// Helper methods for entity/table mapping
|
|
954
|
-
private getEntityNameForTable(tableName:
|
|
955
|
-
const mapping: Record<
|
|
1216
|
+
private getEntityNameForTable(tableName: SUPPORTED_TABLE_NAMES): string | null {
|
|
1217
|
+
const mapping: Record<SUPPORTED_TABLE_NAMES, string> = {
|
|
956
1218
|
[TABLE_THREADS]: 'thread',
|
|
957
1219
|
[TABLE_MESSAGES]: 'message',
|
|
958
1220
|
[TABLE_WORKFLOW_SNAPSHOT]: 'workflowSnapshot',
|
|
@@ -1036,11 +1298,57 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
1036
1298
|
}
|
|
1037
1299
|
});
|
|
1038
1300
|
} catch (error) {
|
|
1039
|
-
|
|
1040
|
-
|
|
1301
|
+
throw new MastraError(
|
|
1302
|
+
{
|
|
1303
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_EVALS_BY_AGENT_NAME_FAILED',
|
|
1304
|
+
domain: ErrorDomain.STORAGE,
|
|
1305
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1306
|
+
details: { agentName },
|
|
1307
|
+
},
|
|
1308
|
+
error,
|
|
1309
|
+
);
|
|
1041
1310
|
}
|
|
1042
1311
|
}
|
|
1043
1312
|
|
|
1313
|
+
async getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & { traces: Trace[] }> {
|
|
1314
|
+
throw new MastraError(
|
|
1315
|
+
{
|
|
1316
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_TRACES_PAGINATED_FAILED',
|
|
1317
|
+
domain: ErrorDomain.STORAGE,
|
|
1318
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1319
|
+
},
|
|
1320
|
+
new Error('Method not implemented.'),
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
async getThreadsByResourceIdPaginated(_args: {
|
|
1325
|
+
resourceId: string;
|
|
1326
|
+
page?: number;
|
|
1327
|
+
perPage?: number;
|
|
1328
|
+
}): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
|
|
1329
|
+
throw new MastraError(
|
|
1330
|
+
{
|
|
1331
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED',
|
|
1332
|
+
domain: ErrorDomain.STORAGE,
|
|
1333
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1334
|
+
},
|
|
1335
|
+
new Error('Method not implemented.'),
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
async getMessagesPaginated(
|
|
1340
|
+
_args: StorageGetMessagesArg,
|
|
1341
|
+
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
1342
|
+
throw new MastraError(
|
|
1343
|
+
{
|
|
1344
|
+
id: 'STORAGE_DYNAMODB_STORE_GET_MESSAGES_PAGINATED_FAILED',
|
|
1345
|
+
domain: ErrorDomain.STORAGE,
|
|
1346
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1347
|
+
},
|
|
1348
|
+
new Error('Method not implemented.'),
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1044
1352
|
/**
|
|
1045
1353
|
* Closes the DynamoDB client connection and cleans up resources.
|
|
1046
1354
|
* Should be called when the store is no longer needed, e.g., at the end of tests or application shutdown.
|
|
@@ -1051,9 +1359,25 @@ export class DynamoDBStore extends MastraStorage {
|
|
|
1051
1359
|
this.client.destroy();
|
|
1052
1360
|
this.logger.debug('DynamoDB client closed successfully for store:', { name: this.name });
|
|
1053
1361
|
} catch (error) {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1362
|
+
throw new MastraError(
|
|
1363
|
+
{
|
|
1364
|
+
id: 'STORAGE_DYNAMODB_STORE_CLOSE_FAILED',
|
|
1365
|
+
domain: ErrorDomain.STORAGE,
|
|
1366
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1367
|
+
},
|
|
1368
|
+
error,
|
|
1369
|
+
);
|
|
1057
1370
|
}
|
|
1058
1371
|
}
|
|
1372
|
+
|
|
1373
|
+
async updateMessages(_args: {
|
|
1374
|
+
messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
|
|
1375
|
+
{
|
|
1376
|
+
id: string;
|
|
1377
|
+
content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
|
|
1378
|
+
}[];
|
|
1379
|
+
}): Promise<MastraMessageV2[]> {
|
|
1380
|
+
this.logger.error('updateMessages is not yet implemented in DynamoDBStore');
|
|
1381
|
+
throw new Error('Method not implemented');
|
|
1382
|
+
}
|
|
1059
1383
|
}
|