@mastra/dynamodb 0.0.0-working-memory-per-user-20250620163010 → 0.0.0-zod-v4-compat-part-2-20250820135355
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/LICENSE.md +11 -42
- package/dist/entities/eval.d.ts +102 -0
- package/dist/entities/eval.d.ts.map +1 -0
- package/dist/entities/index.d.ts +732 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/message.d.ts +100 -0
- package/dist/entities/message.d.ts.map +1 -0
- package/dist/entities/resource.d.ts +54 -0
- package/dist/entities/resource.d.ts.map +1 -0
- package/dist/entities/score.d.ts +215 -0
- package/dist/entities/score.d.ts.map +1 -0
- package/dist/entities/thread.d.ts +69 -0
- package/dist/entities/thread.d.ts.map +1 -0
- package/dist/entities/trace.d.ts +127 -0
- package/dist/entities/trace.d.ts.map +1 -0
- package/dist/entities/utils.d.ts +21 -0
- package/dist/entities/utils.d.ts.map +1 -0
- package/dist/entities/workflow-snapshot.d.ts +74 -0
- package/dist/entities/workflow-snapshot.d.ts.map +1 -0
- package/dist/index.cjs +2176 -516
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2163 -503
- package/dist/index.js.map +1 -0
- package/dist/storage/domains/legacy-evals/index.d.ts +19 -0
- package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +77 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -0
- package/dist/storage/domains/operations/index.d.ts +69 -0
- package/dist/storage/domains/operations/index.d.ts.map +1 -0
- package/dist/storage/domains/score/index.d.ts +42 -0
- package/dist/storage/domains/score/index.d.ts.map +1 -0
- package/dist/storage/domains/traces/index.d.ts +28 -0
- package/dist/storage/domains/traces/index.d.ts.map +1 -0
- package/dist/storage/domains/workflows/index.d.ts +32 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +220 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/package.json +12 -12
- package/src/entities/index.ts +5 -1
- package/src/entities/resource.ts +57 -0
- package/src/entities/score.ts +285 -0
- package/src/storage/domains/legacy-evals/index.ts +243 -0
- package/src/storage/domains/memory/index.ts +894 -0
- package/src/storage/domains/operations/index.ts +433 -0
- package/src/storage/domains/score/index.ts +285 -0
- package/src/storage/domains/traces/index.ts +286 -0
- package/src/storage/domains/workflows/index.ts +297 -0
- package/src/storage/index.test.ts +1347 -1216
- package/src/storage/index.ts +211 -881
- package/dist/_tsup-dts-rollup.d.cts +0 -1157
- package/dist/_tsup-dts-rollup.d.ts +0 -1157
- package/dist/index.d.cts +0 -2
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
|
|
2
2
|
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
3
|
-
import {
|
|
4
|
-
import { MastraStorage, TABLE_TRACES, TABLE_RESOURCES, TABLE_EVALS, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
|
|
3
|
+
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
4
|
+
import { MastraStorage, StoreOperations, TracesStorage, TABLE_TRACES, WorkflowsStorage, MemoryStorage, resolveMessageLimit, ScoresStorage, LegacyEvalsStorage, TABLE_RESOURCES, TABLE_SCORERS, TABLE_EVALS, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
|
|
5
5
|
import { Entity, Service } from 'electrodb';
|
|
6
|
+
import { MessageList } from '@mastra/core/agent';
|
|
6
7
|
|
|
7
8
|
// src/storage/index.ts
|
|
8
9
|
|
|
@@ -293,9 +294,9 @@ var messageEntity = new Entity({
|
|
|
293
294
|
}
|
|
294
295
|
}
|
|
295
296
|
});
|
|
296
|
-
var
|
|
297
|
+
var resourceEntity = new Entity({
|
|
297
298
|
model: {
|
|
298
|
-
entity: "
|
|
299
|
+
entity: "resource",
|
|
299
300
|
version: "1",
|
|
300
301
|
service: "mastra"
|
|
301
302
|
},
|
|
@@ -309,25 +310,21 @@ var threadEntity = new Entity({
|
|
|
309
310
|
type: "string",
|
|
310
311
|
required: true
|
|
311
312
|
},
|
|
312
|
-
|
|
313
|
-
type: "string",
|
|
314
|
-
required: true
|
|
315
|
-
},
|
|
316
|
-
title: {
|
|
313
|
+
workingMemory: {
|
|
317
314
|
type: "string",
|
|
318
|
-
required:
|
|
315
|
+
required: false
|
|
319
316
|
},
|
|
320
317
|
metadata: {
|
|
321
318
|
type: "string",
|
|
322
319
|
required: false,
|
|
323
|
-
// Stringify
|
|
320
|
+
// Stringify content object on set if it's not already a string
|
|
324
321
|
set: (value) => {
|
|
325
322
|
if (value && typeof value !== "string") {
|
|
326
323
|
return JSON.stringify(value);
|
|
327
324
|
}
|
|
328
325
|
return value;
|
|
329
326
|
},
|
|
330
|
-
// Parse JSON string to object on get
|
|
327
|
+
// Parse JSON string to object on get ONLY if it looks like JSON
|
|
331
328
|
get: (value) => {
|
|
332
329
|
if (value && typeof value === "string") {
|
|
333
330
|
try {
|
|
@@ -345,18 +342,13 @@ var threadEntity = new Entity({
|
|
|
345
342
|
indexes: {
|
|
346
343
|
primary: {
|
|
347
344
|
pk: { field: "pk", composite: ["entity", "id"] },
|
|
348
|
-
sk: { field: "sk", composite: ["
|
|
349
|
-
},
|
|
350
|
-
byResource: {
|
|
351
|
-
index: "gsi1",
|
|
352
|
-
pk: { field: "gsi1pk", composite: ["entity", "resourceId"] },
|
|
353
|
-
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
345
|
+
sk: { field: "sk", composite: ["entity"] }
|
|
354
346
|
}
|
|
355
347
|
}
|
|
356
348
|
});
|
|
357
|
-
var
|
|
349
|
+
var scoreEntity = new Entity({
|
|
358
350
|
model: {
|
|
359
|
-
entity: "
|
|
351
|
+
entity: "score",
|
|
360
352
|
version: "1",
|
|
361
353
|
service: "mastra"
|
|
362
354
|
},
|
|
@@ -370,123 +362,275 @@ var traceEntity = new Entity({
|
|
|
370
362
|
type: "string",
|
|
371
363
|
required: true
|
|
372
364
|
},
|
|
373
|
-
|
|
365
|
+
scorerId: {
|
|
366
|
+
type: "string",
|
|
367
|
+
required: true
|
|
368
|
+
},
|
|
369
|
+
traceId: {
|
|
374
370
|
type: "string",
|
|
375
371
|
required: false
|
|
376
372
|
},
|
|
377
|
-
|
|
373
|
+
runId: {
|
|
378
374
|
type: "string",
|
|
379
375
|
required: true
|
|
380
376
|
},
|
|
381
|
-
|
|
377
|
+
scorer: {
|
|
382
378
|
type: "string",
|
|
383
|
-
required: true
|
|
379
|
+
required: true,
|
|
380
|
+
set: (value) => {
|
|
381
|
+
if (value && typeof value !== "string") {
|
|
382
|
+
return JSON.stringify(value);
|
|
383
|
+
}
|
|
384
|
+
return value;
|
|
385
|
+
},
|
|
386
|
+
get: (value) => {
|
|
387
|
+
if (value && typeof value === "string") {
|
|
388
|
+
try {
|
|
389
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
390
|
+
return JSON.parse(value);
|
|
391
|
+
}
|
|
392
|
+
} catch {
|
|
393
|
+
return value;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return value;
|
|
397
|
+
}
|
|
384
398
|
},
|
|
385
|
-
|
|
399
|
+
extractStepResult: {
|
|
386
400
|
type: "string",
|
|
387
|
-
required:
|
|
401
|
+
required: false,
|
|
402
|
+
set: (value) => {
|
|
403
|
+
if (value && typeof value !== "string") {
|
|
404
|
+
return JSON.stringify(value);
|
|
405
|
+
}
|
|
406
|
+
return value;
|
|
407
|
+
},
|
|
408
|
+
get: (value) => {
|
|
409
|
+
if (value && typeof value === "string") {
|
|
410
|
+
try {
|
|
411
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
412
|
+
return JSON.parse(value);
|
|
413
|
+
}
|
|
414
|
+
} catch {
|
|
415
|
+
return value;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return value;
|
|
419
|
+
}
|
|
388
420
|
},
|
|
389
|
-
|
|
421
|
+
analyzeStepResult: {
|
|
422
|
+
type: "string",
|
|
423
|
+
required: false,
|
|
424
|
+
set: (value) => {
|
|
425
|
+
if (value && typeof value !== "string") {
|
|
426
|
+
return JSON.stringify(value);
|
|
427
|
+
}
|
|
428
|
+
return value;
|
|
429
|
+
},
|
|
430
|
+
get: (value) => {
|
|
431
|
+
if (value && typeof value === "string") {
|
|
432
|
+
try {
|
|
433
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
434
|
+
return JSON.parse(value);
|
|
435
|
+
}
|
|
436
|
+
} catch {
|
|
437
|
+
return value;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return value;
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
score: {
|
|
390
444
|
type: "number",
|
|
391
445
|
required: true
|
|
392
446
|
},
|
|
393
|
-
|
|
447
|
+
reason: {
|
|
394
448
|
type: "string",
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
449
|
+
required: false
|
|
450
|
+
},
|
|
451
|
+
extractPrompt: {
|
|
452
|
+
type: "string",
|
|
453
|
+
required: false
|
|
454
|
+
},
|
|
455
|
+
analyzePrompt: {
|
|
456
|
+
type: "string",
|
|
457
|
+
required: false
|
|
458
|
+
},
|
|
459
|
+
reasonPrompt: {
|
|
460
|
+
type: "string",
|
|
461
|
+
required: false
|
|
462
|
+
},
|
|
463
|
+
input: {
|
|
464
|
+
type: "string",
|
|
465
|
+
required: true,
|
|
398
466
|
set: (value) => {
|
|
399
467
|
if (value && typeof value !== "string") {
|
|
400
468
|
return JSON.stringify(value);
|
|
401
469
|
}
|
|
402
470
|
return value;
|
|
403
471
|
},
|
|
404
|
-
// Parse JSON string to object on get
|
|
405
472
|
get: (value) => {
|
|
406
|
-
|
|
473
|
+
if (value && typeof value === "string") {
|
|
474
|
+
try {
|
|
475
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
476
|
+
return JSON.parse(value);
|
|
477
|
+
}
|
|
478
|
+
} catch {
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return value;
|
|
407
483
|
}
|
|
408
484
|
},
|
|
409
|
-
|
|
485
|
+
output: {
|
|
486
|
+
type: "string",
|
|
487
|
+
required: true,
|
|
488
|
+
set: (value) => {
|
|
489
|
+
if (value && typeof value !== "string") {
|
|
490
|
+
return JSON.stringify(value);
|
|
491
|
+
}
|
|
492
|
+
return value;
|
|
493
|
+
},
|
|
494
|
+
get: (value) => {
|
|
495
|
+
if (value && typeof value === "string") {
|
|
496
|
+
try {
|
|
497
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
498
|
+
return JSON.parse(value);
|
|
499
|
+
}
|
|
500
|
+
} catch {
|
|
501
|
+
return value;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return value;
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
additionalContext: {
|
|
410
508
|
type: "string",
|
|
411
|
-
// JSON stringified
|
|
412
509
|
required: false,
|
|
413
|
-
// Stringify object on set
|
|
414
510
|
set: (value) => {
|
|
415
511
|
if (value && typeof value !== "string") {
|
|
416
512
|
return JSON.stringify(value);
|
|
417
513
|
}
|
|
418
514
|
return value;
|
|
419
515
|
},
|
|
420
|
-
// Parse JSON string to object on get
|
|
421
516
|
get: (value) => {
|
|
517
|
+
if (value && typeof value === "string") {
|
|
518
|
+
try {
|
|
519
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
520
|
+
return JSON.parse(value);
|
|
521
|
+
}
|
|
522
|
+
} catch {
|
|
523
|
+
return value;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
422
526
|
return value;
|
|
423
527
|
}
|
|
424
528
|
},
|
|
425
|
-
|
|
529
|
+
runtimeContext: {
|
|
426
530
|
type: "string",
|
|
427
|
-
// JSON stringified
|
|
428
531
|
required: false,
|
|
429
|
-
// Stringify object on set
|
|
430
532
|
set: (value) => {
|
|
431
533
|
if (value && typeof value !== "string") {
|
|
432
534
|
return JSON.stringify(value);
|
|
433
535
|
}
|
|
434
536
|
return value;
|
|
435
537
|
},
|
|
436
|
-
// Parse JSON string to object on get
|
|
437
538
|
get: (value) => {
|
|
539
|
+
if (value && typeof value === "string") {
|
|
540
|
+
try {
|
|
541
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
542
|
+
return JSON.parse(value);
|
|
543
|
+
}
|
|
544
|
+
} catch {
|
|
545
|
+
return value;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
438
548
|
return value;
|
|
439
549
|
}
|
|
440
550
|
},
|
|
441
|
-
|
|
551
|
+
entityType: {
|
|
552
|
+
type: "string",
|
|
553
|
+
required: false
|
|
554
|
+
},
|
|
555
|
+
entityData: {
|
|
442
556
|
type: "string",
|
|
443
|
-
// JSON stringified
|
|
444
557
|
required: false,
|
|
445
|
-
// Stringify object on set
|
|
446
558
|
set: (value) => {
|
|
447
559
|
if (value && typeof value !== "string") {
|
|
448
560
|
return JSON.stringify(value);
|
|
449
561
|
}
|
|
450
562
|
return value;
|
|
451
563
|
},
|
|
452
|
-
// Parse JSON string to object on get
|
|
453
564
|
get: (value) => {
|
|
565
|
+
if (value && typeof value === "string") {
|
|
566
|
+
try {
|
|
567
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
568
|
+
return JSON.parse(value);
|
|
569
|
+
}
|
|
570
|
+
} catch {
|
|
571
|
+
return value;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
454
574
|
return value;
|
|
455
575
|
}
|
|
456
576
|
},
|
|
457
|
-
|
|
577
|
+
entityId: {
|
|
458
578
|
type: "string",
|
|
459
579
|
required: false
|
|
460
580
|
},
|
|
461
|
-
|
|
462
|
-
type: "
|
|
581
|
+
source: {
|
|
582
|
+
type: "string",
|
|
463
583
|
required: true
|
|
464
584
|
},
|
|
465
|
-
|
|
466
|
-
type: "
|
|
467
|
-
required:
|
|
585
|
+
resourceId: {
|
|
586
|
+
type: "string",
|
|
587
|
+
required: false
|
|
588
|
+
},
|
|
589
|
+
threadId: {
|
|
590
|
+
type: "string",
|
|
591
|
+
required: false
|
|
468
592
|
}
|
|
469
593
|
},
|
|
470
594
|
indexes: {
|
|
471
595
|
primary: {
|
|
472
596
|
pk: { field: "pk", composite: ["entity", "id"] },
|
|
473
|
-
sk: { field: "sk", composite: [] }
|
|
597
|
+
sk: { field: "sk", composite: ["entity"] }
|
|
474
598
|
},
|
|
475
|
-
|
|
599
|
+
byScorer: {
|
|
476
600
|
index: "gsi1",
|
|
477
|
-
pk: { field: "gsi1pk", composite: ["entity", "
|
|
478
|
-
sk: { field: "gsi1sk", composite: ["
|
|
601
|
+
pk: { field: "gsi1pk", composite: ["entity", "scorerId"] },
|
|
602
|
+
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
479
603
|
},
|
|
480
|
-
|
|
604
|
+
byRun: {
|
|
481
605
|
index: "gsi2",
|
|
482
|
-
pk: { field: "gsi2pk", composite: ["entity", "
|
|
483
|
-
sk: { field: "gsi2sk", composite: ["
|
|
606
|
+
pk: { field: "gsi2pk", composite: ["entity", "runId"] },
|
|
607
|
+
sk: { field: "gsi2sk", composite: ["createdAt"] }
|
|
608
|
+
},
|
|
609
|
+
byTrace: {
|
|
610
|
+
index: "gsi3",
|
|
611
|
+
pk: { field: "gsi3pk", composite: ["entity", "traceId"] },
|
|
612
|
+
sk: { field: "gsi3sk", composite: ["createdAt"] }
|
|
613
|
+
},
|
|
614
|
+
byEntityData: {
|
|
615
|
+
index: "gsi4",
|
|
616
|
+
pk: { field: "gsi4pk", composite: ["entity", "entityId"] },
|
|
617
|
+
sk: { field: "gsi4sk", composite: ["createdAt"] }
|
|
618
|
+
},
|
|
619
|
+
byResource: {
|
|
620
|
+
index: "gsi5",
|
|
621
|
+
pk: { field: "gsi5pk", composite: ["entity", "resourceId"] },
|
|
622
|
+
sk: { field: "gsi5sk", composite: ["createdAt"] }
|
|
623
|
+
},
|
|
624
|
+
byThread: {
|
|
625
|
+
index: "gsi6",
|
|
626
|
+
pk: { field: "gsi6pk", composite: ["entity", "threadId"] },
|
|
627
|
+
sk: { field: "gsi6sk", composite: ["createdAt"] }
|
|
484
628
|
}
|
|
485
629
|
}
|
|
486
630
|
});
|
|
487
|
-
var
|
|
631
|
+
var threadEntity = new Entity({
|
|
488
632
|
model: {
|
|
489
|
-
entity: "
|
|
633
|
+
entity: "thread",
|
|
490
634
|
version: "1",
|
|
491
635
|
service: "mastra"
|
|
492
636
|
},
|
|
@@ -496,19 +640,22 @@ var workflowSnapshotEntity = new Entity({
|
|
|
496
640
|
required: true
|
|
497
641
|
},
|
|
498
642
|
...baseAttributes,
|
|
499
|
-
|
|
643
|
+
id: {
|
|
500
644
|
type: "string",
|
|
501
645
|
required: true
|
|
502
646
|
},
|
|
503
|
-
|
|
647
|
+
resourceId: {
|
|
504
648
|
type: "string",
|
|
505
649
|
required: true
|
|
506
650
|
},
|
|
507
|
-
|
|
651
|
+
title: {
|
|
508
652
|
type: "string",
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
653
|
+
required: true
|
|
654
|
+
},
|
|
655
|
+
metadata: {
|
|
656
|
+
type: "string",
|
|
657
|
+
required: false,
|
|
658
|
+
// Stringify metadata object on set if it's not already a string
|
|
512
659
|
set: (value) => {
|
|
513
660
|
if (value && typeof value !== "string") {
|
|
514
661
|
return JSON.stringify(value);
|
|
@@ -517,94 +664,1145 @@ var workflowSnapshotEntity = new Entity({
|
|
|
517
664
|
},
|
|
518
665
|
// Parse JSON string to object on get
|
|
519
666
|
get: (value) => {
|
|
520
|
-
|
|
667
|
+
if (value && typeof value === "string") {
|
|
668
|
+
try {
|
|
669
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
670
|
+
return JSON.parse(value);
|
|
671
|
+
}
|
|
672
|
+
} catch {
|
|
673
|
+
return value;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return value;
|
|
521
677
|
}
|
|
522
|
-
},
|
|
523
|
-
resourceId: {
|
|
524
|
-
type: "string",
|
|
525
|
-
required: false
|
|
526
678
|
}
|
|
527
679
|
},
|
|
528
680
|
indexes: {
|
|
529
681
|
primary: {
|
|
530
|
-
pk: { field: "pk", composite: ["entity", "
|
|
531
|
-
sk: { field: "sk", composite: ["
|
|
682
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
683
|
+
sk: { field: "sk", composite: ["id"] }
|
|
532
684
|
},
|
|
533
|
-
|
|
685
|
+
byResource: {
|
|
686
|
+
index: "gsi1",
|
|
687
|
+
pk: { field: "gsi1pk", composite: ["entity", "resourceId"] },
|
|
688
|
+
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
var traceEntity = new Entity({
|
|
693
|
+
model: {
|
|
694
|
+
entity: "trace",
|
|
695
|
+
version: "1",
|
|
696
|
+
service: "mastra"
|
|
697
|
+
},
|
|
698
|
+
attributes: {
|
|
699
|
+
entity: {
|
|
700
|
+
type: "string",
|
|
701
|
+
required: true
|
|
702
|
+
},
|
|
703
|
+
...baseAttributes,
|
|
704
|
+
id: {
|
|
705
|
+
type: "string",
|
|
706
|
+
required: true
|
|
707
|
+
},
|
|
708
|
+
parentSpanId: {
|
|
709
|
+
type: "string",
|
|
710
|
+
required: false
|
|
711
|
+
},
|
|
712
|
+
name: {
|
|
713
|
+
type: "string",
|
|
714
|
+
required: true
|
|
715
|
+
},
|
|
716
|
+
traceId: {
|
|
717
|
+
type: "string",
|
|
718
|
+
required: true
|
|
719
|
+
},
|
|
720
|
+
scope: {
|
|
721
|
+
type: "string",
|
|
722
|
+
required: true
|
|
723
|
+
},
|
|
724
|
+
kind: {
|
|
725
|
+
type: "number",
|
|
726
|
+
required: true
|
|
727
|
+
},
|
|
728
|
+
attributes: {
|
|
729
|
+
type: "string",
|
|
730
|
+
// JSON stringified
|
|
731
|
+
required: false,
|
|
732
|
+
// Stringify object on set
|
|
733
|
+
set: (value) => {
|
|
734
|
+
if (value && typeof value !== "string") {
|
|
735
|
+
return JSON.stringify(value);
|
|
736
|
+
}
|
|
737
|
+
return value;
|
|
738
|
+
},
|
|
739
|
+
// Parse JSON string to object on get
|
|
740
|
+
get: (value) => {
|
|
741
|
+
return value ? JSON.parse(value) : value;
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
status: {
|
|
745
|
+
type: "string",
|
|
746
|
+
// JSON stringified
|
|
747
|
+
required: false,
|
|
748
|
+
// Stringify object on set
|
|
749
|
+
set: (value) => {
|
|
750
|
+
if (value && typeof value !== "string") {
|
|
751
|
+
return JSON.stringify(value);
|
|
752
|
+
}
|
|
753
|
+
return value;
|
|
754
|
+
},
|
|
755
|
+
// Parse JSON string to object on get
|
|
756
|
+
get: (value) => {
|
|
757
|
+
return value;
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
events: {
|
|
761
|
+
type: "string",
|
|
762
|
+
// JSON stringified
|
|
763
|
+
required: false,
|
|
764
|
+
// Stringify object on set
|
|
765
|
+
set: (value) => {
|
|
766
|
+
if (value && typeof value !== "string") {
|
|
767
|
+
return JSON.stringify(value);
|
|
768
|
+
}
|
|
769
|
+
return value;
|
|
770
|
+
},
|
|
771
|
+
// Parse JSON string to object on get
|
|
772
|
+
get: (value) => {
|
|
773
|
+
return value;
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
links: {
|
|
777
|
+
type: "string",
|
|
778
|
+
// JSON stringified
|
|
779
|
+
required: false,
|
|
780
|
+
// Stringify object on set
|
|
781
|
+
set: (value) => {
|
|
782
|
+
if (value && typeof value !== "string") {
|
|
783
|
+
return JSON.stringify(value);
|
|
784
|
+
}
|
|
785
|
+
return value;
|
|
786
|
+
},
|
|
787
|
+
// Parse JSON string to object on get
|
|
788
|
+
get: (value) => {
|
|
789
|
+
return value;
|
|
790
|
+
}
|
|
791
|
+
},
|
|
792
|
+
other: {
|
|
793
|
+
type: "string",
|
|
794
|
+
required: false
|
|
795
|
+
},
|
|
796
|
+
startTime: {
|
|
797
|
+
type: "number",
|
|
798
|
+
required: true
|
|
799
|
+
},
|
|
800
|
+
endTime: {
|
|
801
|
+
type: "number",
|
|
802
|
+
required: true
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
indexes: {
|
|
806
|
+
primary: {
|
|
807
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
808
|
+
sk: { field: "sk", composite: [] }
|
|
809
|
+
},
|
|
810
|
+
byName: {
|
|
811
|
+
index: "gsi1",
|
|
812
|
+
pk: { field: "gsi1pk", composite: ["entity", "name"] },
|
|
813
|
+
sk: { field: "gsi1sk", composite: ["startTime"] }
|
|
814
|
+
},
|
|
815
|
+
byScope: {
|
|
816
|
+
index: "gsi2",
|
|
817
|
+
pk: { field: "gsi2pk", composite: ["entity", "scope"] },
|
|
818
|
+
sk: { field: "gsi2sk", composite: ["startTime"] }
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
var workflowSnapshotEntity = new Entity({
|
|
823
|
+
model: {
|
|
824
|
+
entity: "workflow_snapshot",
|
|
825
|
+
version: "1",
|
|
826
|
+
service: "mastra"
|
|
827
|
+
},
|
|
828
|
+
attributes: {
|
|
829
|
+
entity: {
|
|
830
|
+
type: "string",
|
|
831
|
+
required: true
|
|
832
|
+
},
|
|
833
|
+
...baseAttributes,
|
|
834
|
+
workflow_name: {
|
|
835
|
+
type: "string",
|
|
836
|
+
required: true
|
|
837
|
+
},
|
|
838
|
+
run_id: {
|
|
839
|
+
type: "string",
|
|
840
|
+
required: true
|
|
841
|
+
},
|
|
842
|
+
snapshot: {
|
|
843
|
+
type: "string",
|
|
844
|
+
// JSON stringified
|
|
845
|
+
required: true,
|
|
846
|
+
// Stringify snapshot object on set
|
|
847
|
+
set: (value) => {
|
|
848
|
+
if (value && typeof value !== "string") {
|
|
849
|
+
return JSON.stringify(value);
|
|
850
|
+
}
|
|
851
|
+
return value;
|
|
852
|
+
},
|
|
853
|
+
// Parse JSON string to object on get
|
|
854
|
+
get: (value) => {
|
|
855
|
+
return value ? JSON.parse(value) : value;
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
resourceId: {
|
|
859
|
+
type: "string",
|
|
860
|
+
required: false
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
indexes: {
|
|
864
|
+
primary: {
|
|
865
|
+
pk: { field: "pk", composite: ["entity", "workflow_name"] },
|
|
866
|
+
sk: { field: "sk", composite: ["run_id"] }
|
|
867
|
+
},
|
|
868
|
+
// GSI to allow querying by run_id efficiently without knowing the workflow_name
|
|
534
869
|
gsi2: {
|
|
535
870
|
index: "gsi2",
|
|
536
871
|
pk: { field: "gsi2pk", composite: ["entity", "run_id"] },
|
|
537
872
|
sk: { field: "gsi2sk", composite: ["workflow_name"] }
|
|
538
873
|
}
|
|
539
874
|
}
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
// src/entities/index.ts
|
|
543
|
-
function getElectroDbService(client, tableName) {
|
|
544
|
-
return new Service(
|
|
545
|
-
{
|
|
546
|
-
thread: threadEntity,
|
|
547
|
-
message: messageEntity,
|
|
548
|
-
eval: evalEntity,
|
|
549
|
-
trace: traceEntity,
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
var
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
service
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
// src/entities/index.ts
|
|
878
|
+
function getElectroDbService(client, tableName) {
|
|
879
|
+
return new Service(
|
|
880
|
+
{
|
|
881
|
+
thread: threadEntity,
|
|
882
|
+
message: messageEntity,
|
|
883
|
+
eval: evalEntity,
|
|
884
|
+
trace: traceEntity,
|
|
885
|
+
workflow_snapshot: workflowSnapshotEntity,
|
|
886
|
+
resource: resourceEntity,
|
|
887
|
+
score: scoreEntity
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
client,
|
|
891
|
+
table: tableName
|
|
892
|
+
}
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
var LegacyEvalsDynamoDB = class extends LegacyEvalsStorage {
|
|
896
|
+
service;
|
|
897
|
+
tableName;
|
|
898
|
+
constructor({ service, tableName }) {
|
|
899
|
+
super();
|
|
900
|
+
this.service = service;
|
|
901
|
+
this.tableName = tableName;
|
|
902
|
+
}
|
|
903
|
+
// Eval operations
|
|
904
|
+
async getEvalsByAgentName(agentName, type) {
|
|
905
|
+
this.logger.debug("Getting evals for agent", { agentName, type });
|
|
906
|
+
try {
|
|
907
|
+
const query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
|
|
908
|
+
const results = await query.go({ order: "desc", limit: 100 });
|
|
909
|
+
if (!results.data.length) {
|
|
910
|
+
return [];
|
|
911
|
+
}
|
|
912
|
+
let filteredData = results.data;
|
|
913
|
+
if (type) {
|
|
914
|
+
filteredData = filteredData.filter((evalRecord) => {
|
|
915
|
+
try {
|
|
916
|
+
const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
|
|
917
|
+
if (type === "test" && !testInfo) {
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
if (type === "live" && testInfo) {
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
} catch (e) {
|
|
924
|
+
this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
|
|
925
|
+
}
|
|
926
|
+
return true;
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
return filteredData.map((evalRecord) => {
|
|
930
|
+
try {
|
|
931
|
+
return {
|
|
932
|
+
input: evalRecord.input,
|
|
933
|
+
output: evalRecord.output,
|
|
934
|
+
// Safely parse result and test_info
|
|
935
|
+
result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
|
|
936
|
+
agentName: evalRecord.agent_name,
|
|
937
|
+
createdAt: evalRecord.created_at,
|
|
938
|
+
// Keep as string from DDB?
|
|
939
|
+
metricName: evalRecord.metric_name,
|
|
940
|
+
instructions: evalRecord.instructions,
|
|
941
|
+
runId: evalRecord.run_id,
|
|
942
|
+
globalRunId: evalRecord.global_run_id,
|
|
943
|
+
testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
|
|
944
|
+
};
|
|
945
|
+
} catch (parseError) {
|
|
946
|
+
this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
|
|
947
|
+
return {
|
|
948
|
+
agentName: evalRecord.agent_name,
|
|
949
|
+
createdAt: evalRecord.created_at,
|
|
950
|
+
runId: evalRecord.run_id,
|
|
951
|
+
globalRunId: evalRecord.global_run_id
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
} catch (error) {
|
|
956
|
+
throw new MastraError(
|
|
957
|
+
{
|
|
958
|
+
id: "STORAGE_DYNAMODB_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
|
|
959
|
+
domain: ErrorDomain.STORAGE,
|
|
960
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
961
|
+
details: { agentName }
|
|
962
|
+
},
|
|
963
|
+
error
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
async getEvals(options = {}) {
|
|
968
|
+
const { agentName, type, page = 0, perPage = 100, dateRange } = options;
|
|
969
|
+
this.logger.debug("Getting evals with pagination", { agentName, type, page, perPage, dateRange });
|
|
970
|
+
try {
|
|
971
|
+
let query;
|
|
972
|
+
if (agentName) {
|
|
973
|
+
query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
|
|
974
|
+
} else {
|
|
975
|
+
query = this.service.entities.eval.query.byEntity({ entity: "eval" });
|
|
976
|
+
}
|
|
977
|
+
const results = await query.go({
|
|
978
|
+
order: "desc",
|
|
979
|
+
pages: "all"
|
|
980
|
+
// Get all pages to apply filtering and pagination
|
|
981
|
+
});
|
|
982
|
+
if (!results.data.length) {
|
|
983
|
+
return {
|
|
984
|
+
evals: [],
|
|
985
|
+
total: 0,
|
|
986
|
+
page,
|
|
987
|
+
perPage,
|
|
988
|
+
hasMore: false
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
let filteredData = results.data;
|
|
992
|
+
if (type) {
|
|
993
|
+
filteredData = filteredData.filter((evalRecord) => {
|
|
994
|
+
try {
|
|
995
|
+
const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
|
|
996
|
+
if (type === "test" && !testInfo) {
|
|
997
|
+
return false;
|
|
998
|
+
}
|
|
999
|
+
if (type === "live" && testInfo) {
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
1002
|
+
} catch (e) {
|
|
1003
|
+
this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
|
|
1004
|
+
}
|
|
1005
|
+
return true;
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
if (dateRange) {
|
|
1009
|
+
const fromDate = dateRange.start;
|
|
1010
|
+
const toDate = dateRange.end;
|
|
1011
|
+
filteredData = filteredData.filter((evalRecord) => {
|
|
1012
|
+
const recordDate = new Date(evalRecord.created_at);
|
|
1013
|
+
if (fromDate && recordDate < fromDate) {
|
|
1014
|
+
return false;
|
|
1015
|
+
}
|
|
1016
|
+
if (toDate && recordDate > toDate) {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
return true;
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
const total = filteredData.length;
|
|
1023
|
+
const start = page * perPage;
|
|
1024
|
+
const end = start + perPage;
|
|
1025
|
+
const paginatedData = filteredData.slice(start, end);
|
|
1026
|
+
const evals = paginatedData.map((evalRecord) => {
|
|
1027
|
+
try {
|
|
1028
|
+
return {
|
|
1029
|
+
input: evalRecord.input,
|
|
1030
|
+
output: evalRecord.output,
|
|
1031
|
+
result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
|
|
1032
|
+
agentName: evalRecord.agent_name,
|
|
1033
|
+
createdAt: evalRecord.created_at,
|
|
1034
|
+
metricName: evalRecord.metric_name,
|
|
1035
|
+
instructions: evalRecord.instructions,
|
|
1036
|
+
runId: evalRecord.run_id,
|
|
1037
|
+
globalRunId: evalRecord.global_run_id,
|
|
1038
|
+
testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
|
|
1039
|
+
};
|
|
1040
|
+
} catch (parseError) {
|
|
1041
|
+
this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
|
|
1042
|
+
return {
|
|
1043
|
+
agentName: evalRecord.agent_name,
|
|
1044
|
+
createdAt: evalRecord.created_at,
|
|
1045
|
+
runId: evalRecord.run_id,
|
|
1046
|
+
globalRunId: evalRecord.global_run_id
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
const hasMore = end < total;
|
|
1051
|
+
return {
|
|
1052
|
+
evals,
|
|
1053
|
+
total,
|
|
1054
|
+
page,
|
|
1055
|
+
perPage,
|
|
1056
|
+
hasMore
|
|
1057
|
+
};
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
throw new MastraError(
|
|
1060
|
+
{
|
|
1061
|
+
id: "STORAGE_DYNAMODB_STORE_GET_EVALS_FAILED",
|
|
1062
|
+
domain: ErrorDomain.STORAGE,
|
|
1063
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1064
|
+
details: {
|
|
1065
|
+
agentName: agentName || "all",
|
|
1066
|
+
type: type || "all",
|
|
1067
|
+
page,
|
|
1068
|
+
perPage
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
error
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
var MemoryStorageDynamoDB = class extends MemoryStorage {
|
|
1077
|
+
service;
|
|
1078
|
+
constructor({ service }) {
|
|
1079
|
+
super();
|
|
1080
|
+
this.service = service;
|
|
1081
|
+
}
|
|
1082
|
+
// Helper function to parse message data (handle JSON fields)
|
|
1083
|
+
parseMessageData(data) {
|
|
1084
|
+
return {
|
|
1085
|
+
...data,
|
|
1086
|
+
// Ensure dates are Date objects if needed (ElectroDB might return strings)
|
|
1087
|
+
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
1088
|
+
updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0
|
|
1089
|
+
// Other fields like content, toolCallArgs etc. are assumed to be correctly
|
|
1090
|
+
// transformed by the ElectroDB entity getters.
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
async getThreadById({ threadId }) {
|
|
1094
|
+
this.logger.debug("Getting thread by ID", { threadId });
|
|
1095
|
+
try {
|
|
1096
|
+
const result = await this.service.entities.thread.get({ entity: "thread", id: threadId }).go();
|
|
1097
|
+
if (!result.data) {
|
|
1098
|
+
return null;
|
|
1099
|
+
}
|
|
1100
|
+
const data = result.data;
|
|
1101
|
+
return {
|
|
1102
|
+
...data,
|
|
1103
|
+
// Convert date strings back to Date objects for consistency
|
|
1104
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
1105
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
1106
|
+
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
1107
|
+
// metadata is already transformed by the entity's getter
|
|
1108
|
+
};
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
throw new MastraError(
|
|
1111
|
+
{
|
|
1112
|
+
id: "STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED",
|
|
1113
|
+
domain: ErrorDomain.STORAGE,
|
|
1114
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1115
|
+
details: { threadId }
|
|
1116
|
+
},
|
|
1117
|
+
error
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
async getThreadsByResourceId({ resourceId }) {
|
|
1122
|
+
this.logger.debug("Getting threads by resource ID", { resourceId });
|
|
1123
|
+
try {
|
|
1124
|
+
const result = await this.service.entities.thread.query.byResource({ entity: "thread", resourceId }).go();
|
|
1125
|
+
if (!result.data.length) {
|
|
1126
|
+
return [];
|
|
1127
|
+
}
|
|
1128
|
+
return result.data.map((data) => ({
|
|
1129
|
+
...data,
|
|
1130
|
+
// Convert date strings back to Date objects for consistency
|
|
1131
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
1132
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
1133
|
+
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
1134
|
+
// metadata is already transformed by the entity's getter
|
|
1135
|
+
}));
|
|
1136
|
+
} catch (error) {
|
|
1137
|
+
throw new MastraError(
|
|
1138
|
+
{
|
|
1139
|
+
id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
|
|
1140
|
+
domain: ErrorDomain.STORAGE,
|
|
1141
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1142
|
+
details: { resourceId }
|
|
1143
|
+
},
|
|
1144
|
+
error
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
async saveThread({ thread }) {
|
|
1149
|
+
this.logger.debug("Saving thread", { threadId: thread.id });
|
|
1150
|
+
const now = /* @__PURE__ */ new Date();
|
|
1151
|
+
const threadData = {
|
|
1152
|
+
entity: "thread",
|
|
1153
|
+
id: thread.id,
|
|
1154
|
+
resourceId: thread.resourceId,
|
|
1155
|
+
title: thread.title || `Thread ${thread.id}`,
|
|
1156
|
+
createdAt: thread.createdAt?.toISOString() || now.toISOString(),
|
|
1157
|
+
updatedAt: now.toISOString(),
|
|
1158
|
+
metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
|
|
1159
|
+
};
|
|
1160
|
+
try {
|
|
1161
|
+
await this.service.entities.thread.upsert(threadData).go();
|
|
1162
|
+
return {
|
|
1163
|
+
id: thread.id,
|
|
1164
|
+
resourceId: thread.resourceId,
|
|
1165
|
+
title: threadData.title,
|
|
1166
|
+
createdAt: thread.createdAt || now,
|
|
1167
|
+
updatedAt: now,
|
|
1168
|
+
metadata: thread.metadata
|
|
1169
|
+
};
|
|
1170
|
+
} catch (error) {
|
|
1171
|
+
throw new MastraError(
|
|
1172
|
+
{
|
|
1173
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED",
|
|
1174
|
+
domain: ErrorDomain.STORAGE,
|
|
1175
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1176
|
+
details: { threadId: thread.id }
|
|
1177
|
+
},
|
|
1178
|
+
error
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
async updateThread({
|
|
1183
|
+
id,
|
|
1184
|
+
title,
|
|
1185
|
+
metadata
|
|
1186
|
+
}) {
|
|
1187
|
+
this.logger.debug("Updating thread", { threadId: id });
|
|
1188
|
+
try {
|
|
1189
|
+
const existingThread = await this.getThreadById({ threadId: id });
|
|
1190
|
+
if (!existingThread) {
|
|
1191
|
+
throw new Error(`Thread not found: ${id}`);
|
|
1192
|
+
}
|
|
1193
|
+
const now = /* @__PURE__ */ new Date();
|
|
1194
|
+
const updateData = {
|
|
1195
|
+
updatedAt: now.toISOString()
|
|
1196
|
+
};
|
|
1197
|
+
if (title) {
|
|
1198
|
+
updateData.title = title;
|
|
1199
|
+
}
|
|
1200
|
+
if (metadata) {
|
|
1201
|
+
const existingMetadata = existingThread.metadata ? typeof existingThread.metadata === "string" ? JSON.parse(existingThread.metadata) : existingThread.metadata : {};
|
|
1202
|
+
const mergedMetadata = { ...existingMetadata, ...metadata };
|
|
1203
|
+
updateData.metadata = JSON.stringify(mergedMetadata);
|
|
1204
|
+
}
|
|
1205
|
+
await this.service.entities.thread.update({ entity: "thread", id }).set(updateData).go();
|
|
1206
|
+
return {
|
|
1207
|
+
...existingThread,
|
|
1208
|
+
title: title || existingThread.title,
|
|
1209
|
+
metadata: metadata ? { ...existingThread.metadata, ...metadata } : existingThread.metadata,
|
|
1210
|
+
updatedAt: now
|
|
1211
|
+
};
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
throw new MastraError(
|
|
1214
|
+
{
|
|
1215
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED",
|
|
1216
|
+
domain: ErrorDomain.STORAGE,
|
|
1217
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1218
|
+
details: { threadId: id }
|
|
1219
|
+
},
|
|
1220
|
+
error
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
async deleteThread({ threadId }) {
|
|
1225
|
+
this.logger.debug("Deleting thread", { threadId });
|
|
1226
|
+
try {
|
|
1227
|
+
const messages = await this.getMessages({ threadId });
|
|
1228
|
+
if (messages.length > 0) {
|
|
1229
|
+
const batchSize = 25;
|
|
1230
|
+
for (let i = 0; i < messages.length; i += batchSize) {
|
|
1231
|
+
const batch = messages.slice(i, i + batchSize);
|
|
1232
|
+
await Promise.all(
|
|
1233
|
+
batch.map(
|
|
1234
|
+
(message) => this.service.entities.message.delete({
|
|
1235
|
+
entity: "message",
|
|
1236
|
+
id: message.id,
|
|
1237
|
+
threadId: message.threadId
|
|
1238
|
+
}).go()
|
|
1239
|
+
)
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
await this.service.entities.thread.delete({ entity: "thread", id: threadId }).go();
|
|
1244
|
+
} catch (error) {
|
|
1245
|
+
throw new MastraError(
|
|
1246
|
+
{
|
|
1247
|
+
id: "STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED",
|
|
1248
|
+
domain: ErrorDomain.STORAGE,
|
|
1249
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1250
|
+
details: { threadId }
|
|
1251
|
+
},
|
|
1252
|
+
error
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
async getMessages({
|
|
1257
|
+
threadId,
|
|
1258
|
+
resourceId,
|
|
1259
|
+
selectBy,
|
|
1260
|
+
format
|
|
1261
|
+
}) {
|
|
1262
|
+
this.logger.debug("Getting messages", { threadId, selectBy });
|
|
1263
|
+
try {
|
|
1264
|
+
const messages = [];
|
|
1265
|
+
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
1266
|
+
if (selectBy?.include?.length) {
|
|
1267
|
+
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1268
|
+
if (includeMessages) {
|
|
1269
|
+
messages.push(...includeMessages);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
if (limit !== 0) {
|
|
1273
|
+
const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
|
|
1274
|
+
let results;
|
|
1275
|
+
if (limit !== Number.MAX_SAFE_INTEGER && limit > 0) {
|
|
1276
|
+
results = await query.go({ limit, order: "desc" });
|
|
1277
|
+
results.data = results.data.reverse();
|
|
1278
|
+
} else {
|
|
1279
|
+
results = await query.go();
|
|
1280
|
+
}
|
|
1281
|
+
let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg);
|
|
1282
|
+
allThreadMessages.sort((a, b) => {
|
|
1283
|
+
const timeA = a.createdAt.getTime();
|
|
1284
|
+
const timeB = b.createdAt.getTime();
|
|
1285
|
+
if (timeA === timeB) {
|
|
1286
|
+
return a.id.localeCompare(b.id);
|
|
1287
|
+
}
|
|
1288
|
+
return timeA - timeB;
|
|
1289
|
+
});
|
|
1290
|
+
messages.push(...allThreadMessages);
|
|
1291
|
+
}
|
|
1292
|
+
messages.sort((a, b) => {
|
|
1293
|
+
const timeA = a.createdAt.getTime();
|
|
1294
|
+
const timeB = b.createdAt.getTime();
|
|
1295
|
+
if (timeA === timeB) {
|
|
1296
|
+
return a.id.localeCompare(b.id);
|
|
1297
|
+
}
|
|
1298
|
+
return timeA - timeB;
|
|
1299
|
+
});
|
|
1300
|
+
const uniqueMessages = messages.filter(
|
|
1301
|
+
(message, index, self) => index === self.findIndex((m) => m.id === message.id)
|
|
1302
|
+
);
|
|
1303
|
+
const list = new MessageList({ threadId, resourceId }).add(uniqueMessages, "memory");
|
|
1304
|
+
if (format === `v2`) return list.get.all.v2();
|
|
1305
|
+
return list.get.all.v1();
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
throw new MastraError(
|
|
1308
|
+
{
|
|
1309
|
+
id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_FAILED",
|
|
1310
|
+
domain: ErrorDomain.STORAGE,
|
|
1311
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1312
|
+
details: { threadId }
|
|
1313
|
+
},
|
|
1314
|
+
error
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
async saveMessages(args) {
|
|
1319
|
+
const { messages, format = "v1" } = args;
|
|
1320
|
+
this.logger.debug("Saving messages", { count: messages.length });
|
|
1321
|
+
if (!messages.length) {
|
|
1322
|
+
return [];
|
|
1323
|
+
}
|
|
1324
|
+
const threadId = messages[0]?.threadId;
|
|
1325
|
+
if (!threadId) {
|
|
1326
|
+
throw new Error("Thread ID is required");
|
|
1327
|
+
}
|
|
1328
|
+
const messagesToSave = messages.map((msg) => {
|
|
1329
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1330
|
+
return {
|
|
1331
|
+
entity: "message",
|
|
1332
|
+
// Add entity type
|
|
1333
|
+
id: msg.id,
|
|
1334
|
+
threadId: msg.threadId,
|
|
1335
|
+
role: msg.role,
|
|
1336
|
+
type: msg.type,
|
|
1337
|
+
resourceId: msg.resourceId,
|
|
1338
|
+
// Ensure complex fields are stringified if not handled by attribute setters
|
|
1339
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
1340
|
+
toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
|
|
1341
|
+
toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
|
|
1342
|
+
toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
|
|
1343
|
+
createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
|
|
1344
|
+
updatedAt: now
|
|
1345
|
+
// Add updatedAt
|
|
1346
|
+
};
|
|
1347
|
+
});
|
|
1348
|
+
try {
|
|
1349
|
+
const savedMessageIds = [];
|
|
1350
|
+
for (const messageData of messagesToSave) {
|
|
1351
|
+
if (!messageData.entity) {
|
|
1352
|
+
this.logger.error("Missing entity property in message data for create", { messageData });
|
|
1353
|
+
throw new Error("Internal error: Missing entity property during saveMessages");
|
|
1354
|
+
}
|
|
1355
|
+
try {
|
|
1356
|
+
await this.service.entities.message.put(messageData).go();
|
|
1357
|
+
savedMessageIds.push(messageData.id);
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
for (const savedId of savedMessageIds) {
|
|
1360
|
+
try {
|
|
1361
|
+
await this.service.entities.message.delete({ entity: "message", id: savedId }).go();
|
|
1362
|
+
} catch (rollbackError) {
|
|
1363
|
+
this.logger.error("Failed to rollback message during save error", {
|
|
1364
|
+
messageId: savedId,
|
|
1365
|
+
error: rollbackError
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
throw error;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
|
|
1373
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1374
|
+
}).go();
|
|
1375
|
+
const list = new MessageList().add(messages, "memory");
|
|
1376
|
+
if (format === `v1`) return list.get.all.v1();
|
|
1377
|
+
return list.get.all.v2();
|
|
1378
|
+
} catch (error) {
|
|
1379
|
+
throw new MastraError(
|
|
1380
|
+
{
|
|
1381
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED",
|
|
1382
|
+
domain: ErrorDomain.STORAGE,
|
|
1383
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1384
|
+
details: { count: messages.length }
|
|
1385
|
+
},
|
|
1386
|
+
error
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
1391
|
+
const { resourceId, page = 0, perPage = 100 } = args;
|
|
1392
|
+
this.logger.debug("Getting threads by resource ID with pagination", { resourceId, page, perPage });
|
|
1393
|
+
try {
|
|
1394
|
+
const query = this.service.entities.thread.query.byResource({ entity: "thread", resourceId });
|
|
1395
|
+
const results = await query.go();
|
|
1396
|
+
const allThreads = results.data;
|
|
1397
|
+
const startIndex = page * perPage;
|
|
1398
|
+
const endIndex = startIndex + perPage;
|
|
1399
|
+
const paginatedThreads = allThreads.slice(startIndex, endIndex);
|
|
1400
|
+
const total = allThreads.length;
|
|
1401
|
+
const hasMore = endIndex < total;
|
|
1402
|
+
return {
|
|
1403
|
+
threads: paginatedThreads,
|
|
1404
|
+
total,
|
|
1405
|
+
page,
|
|
1406
|
+
perPage,
|
|
1407
|
+
hasMore
|
|
1408
|
+
};
|
|
1409
|
+
} catch (error) {
|
|
1410
|
+
throw new MastraError(
|
|
1411
|
+
{
|
|
1412
|
+
id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
|
|
1413
|
+
domain: ErrorDomain.STORAGE,
|
|
1414
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1415
|
+
details: { resourceId, page, perPage }
|
|
1416
|
+
},
|
|
1417
|
+
error
|
|
1418
|
+
);
|
|
569
1419
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
1420
|
+
}
|
|
1421
|
+
async getMessagesPaginated(args) {
|
|
1422
|
+
const { threadId, resourceId, selectBy, format = "v1" } = args;
|
|
1423
|
+
const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
|
|
1424
|
+
const fromDate = dateRange?.start;
|
|
1425
|
+
const toDate = dateRange?.end;
|
|
1426
|
+
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
1427
|
+
this.logger.debug("Getting messages with pagination", { threadId, page, perPage, fromDate, toDate, limit });
|
|
1428
|
+
try {
|
|
1429
|
+
let messages = [];
|
|
1430
|
+
if (selectBy?.include?.length) {
|
|
1431
|
+
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1432
|
+
if (includeMessages) {
|
|
1433
|
+
messages.push(...includeMessages);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (limit !== 0) {
|
|
1437
|
+
const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
|
|
1438
|
+
let results;
|
|
1439
|
+
if (limit !== Number.MAX_SAFE_INTEGER && limit > 0) {
|
|
1440
|
+
results = await query.go({ limit, order: "desc" });
|
|
1441
|
+
results.data = results.data.reverse();
|
|
1442
|
+
} else {
|
|
1443
|
+
results = await query.go();
|
|
1444
|
+
}
|
|
1445
|
+
let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg);
|
|
1446
|
+
allThreadMessages.sort((a, b) => {
|
|
1447
|
+
const timeA = a.createdAt.getTime();
|
|
1448
|
+
const timeB = b.createdAt.getTime();
|
|
1449
|
+
if (timeA === timeB) {
|
|
1450
|
+
return a.id.localeCompare(b.id);
|
|
1451
|
+
}
|
|
1452
|
+
return timeA - timeB;
|
|
1453
|
+
});
|
|
1454
|
+
const excludeIds = messages.map((m) => m.id);
|
|
1455
|
+
if (excludeIds.length > 0) {
|
|
1456
|
+
allThreadMessages = allThreadMessages.filter((msg) => !excludeIds.includes(msg.id));
|
|
1457
|
+
}
|
|
1458
|
+
messages.push(...allThreadMessages);
|
|
1459
|
+
}
|
|
1460
|
+
messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
1461
|
+
if (fromDate || toDate) {
|
|
1462
|
+
messages = messages.filter((msg) => {
|
|
1463
|
+
const createdAt = new Date(msg.createdAt).getTime();
|
|
1464
|
+
if (fromDate && createdAt < new Date(fromDate).getTime()) return false;
|
|
1465
|
+
if (toDate && createdAt > new Date(toDate).getTime()) return false;
|
|
1466
|
+
return true;
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
const total = messages.length;
|
|
1470
|
+
const start = page * perPage;
|
|
1471
|
+
const end = start + perPage;
|
|
1472
|
+
const paginatedMessages = messages.slice(start, end);
|
|
1473
|
+
const hasMore = end < total;
|
|
1474
|
+
const list = new MessageList({ threadId, resourceId }).add(paginatedMessages, "memory");
|
|
1475
|
+
const finalMessages = format === "v2" ? list.get.all.v2() : list.get.all.v1();
|
|
1476
|
+
return {
|
|
1477
|
+
messages: finalMessages,
|
|
1478
|
+
total,
|
|
1479
|
+
page,
|
|
1480
|
+
perPage,
|
|
1481
|
+
hasMore
|
|
1482
|
+
};
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
throw new MastraError(
|
|
1485
|
+
{
|
|
1486
|
+
id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_PAGINATED_FAILED",
|
|
1487
|
+
domain: ErrorDomain.STORAGE,
|
|
1488
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1489
|
+
details: { threadId }
|
|
1490
|
+
},
|
|
1491
|
+
error
|
|
573
1492
|
);
|
|
574
1493
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
1494
|
+
}
|
|
1495
|
+
// Helper method to get included messages with context
|
|
1496
|
+
async _getIncludedMessages(threadId, selectBy) {
|
|
1497
|
+
if (!selectBy?.include?.length) {
|
|
1498
|
+
return [];
|
|
1499
|
+
}
|
|
1500
|
+
const includeMessages = [];
|
|
1501
|
+
for (const includeItem of selectBy.include) {
|
|
1502
|
+
try {
|
|
1503
|
+
const { id, threadId: targetThreadId, withPreviousMessages = 0, withNextMessages = 0 } = includeItem;
|
|
1504
|
+
const searchThreadId = targetThreadId || threadId;
|
|
1505
|
+
this.logger.debug("Getting included messages for", {
|
|
1506
|
+
id,
|
|
1507
|
+
targetThreadId,
|
|
1508
|
+
searchThreadId,
|
|
1509
|
+
withPreviousMessages,
|
|
1510
|
+
withNextMessages
|
|
1511
|
+
});
|
|
1512
|
+
const query = this.service.entities.message.query.byThread({ entity: "message", threadId: searchThreadId });
|
|
1513
|
+
const results = await query.go();
|
|
1514
|
+
const allMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg && typeof msg.content === "object");
|
|
1515
|
+
this.logger.debug("Found messages in thread", {
|
|
1516
|
+
threadId: searchThreadId,
|
|
1517
|
+
messageCount: allMessages.length,
|
|
1518
|
+
messageIds: allMessages.map((m) => m.id)
|
|
1519
|
+
});
|
|
1520
|
+
allMessages.sort((a, b) => {
|
|
1521
|
+
const timeA = a.createdAt.getTime();
|
|
1522
|
+
const timeB = b.createdAt.getTime();
|
|
1523
|
+
if (timeA === timeB) {
|
|
1524
|
+
return a.id.localeCompare(b.id);
|
|
1525
|
+
}
|
|
1526
|
+
return timeA - timeB;
|
|
1527
|
+
});
|
|
1528
|
+
const targetIndex = allMessages.findIndex((msg) => msg.id === id);
|
|
1529
|
+
if (targetIndex === -1) {
|
|
1530
|
+
this.logger.warn("Target message not found", { id, threadId: searchThreadId });
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
this.logger.debug("Found target message at index", { id, targetIndex, totalMessages: allMessages.length });
|
|
1534
|
+
const startIndex = Math.max(0, targetIndex - withPreviousMessages);
|
|
1535
|
+
const endIndex = Math.min(allMessages.length, targetIndex + withNextMessages + 1);
|
|
1536
|
+
const contextMessages = allMessages.slice(startIndex, endIndex);
|
|
1537
|
+
this.logger.debug("Context messages", {
|
|
1538
|
+
startIndex,
|
|
1539
|
+
endIndex,
|
|
1540
|
+
contextCount: contextMessages.length,
|
|
1541
|
+
contextIds: contextMessages.map((m) => m.id)
|
|
1542
|
+
});
|
|
1543
|
+
includeMessages.push(...contextMessages);
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
this.logger.warn("Failed to get included message", { messageId: includeItem.id, error });
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
this.logger.debug("Total included messages", {
|
|
1549
|
+
count: includeMessages.length,
|
|
1550
|
+
ids: includeMessages.map((m) => m.id)
|
|
579
1551
|
});
|
|
580
|
-
|
|
581
|
-
this.client = DynamoDBDocumentClient.from(dynamoClient);
|
|
582
|
-
this.service = getElectroDbService(this.client, this.tableName);
|
|
1552
|
+
return includeMessages;
|
|
583
1553
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
this.logger.debug("Validating access to externally managed table", { tableName, physicalTable: this.tableName });
|
|
1554
|
+
async updateMessages(args) {
|
|
1555
|
+
const { messages } = args;
|
|
1556
|
+
this.logger.debug("Updating messages", { count: messages.length });
|
|
1557
|
+
if (!messages.length) {
|
|
1558
|
+
return [];
|
|
1559
|
+
}
|
|
1560
|
+
const updatedMessages = [];
|
|
1561
|
+
const affectedThreadIds = /* @__PURE__ */ new Set();
|
|
593
1562
|
try {
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
this.
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
);
|
|
1563
|
+
for (const updateData of messages) {
|
|
1564
|
+
const { id, ...updates } = updateData;
|
|
1565
|
+
const existingMessage = await this.service.entities.message.get({ entity: "message", id }).go();
|
|
1566
|
+
if (!existingMessage.data) {
|
|
1567
|
+
this.logger.warn("Message not found for update", { id });
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
const existingMsg = this.parseMessageData(existingMessage.data);
|
|
1571
|
+
const originalThreadId = existingMsg.threadId;
|
|
1572
|
+
affectedThreadIds.add(originalThreadId);
|
|
1573
|
+
const updatePayload = {
|
|
1574
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1575
|
+
};
|
|
1576
|
+
if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
|
|
1577
|
+
if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
|
|
1578
|
+
if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
|
|
1579
|
+
if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
|
|
1580
|
+
updatePayload.threadId = updates.threadId;
|
|
1581
|
+
affectedThreadIds.add(updates.threadId);
|
|
1582
|
+
}
|
|
1583
|
+
if (updates.content) {
|
|
1584
|
+
const existingContent = existingMsg.content;
|
|
1585
|
+
let newContent = { ...existingContent };
|
|
1586
|
+
if (updates.content.metadata !== void 0) {
|
|
1587
|
+
newContent.metadata = {
|
|
1588
|
+
...existingContent.metadata || {},
|
|
1589
|
+
...updates.content.metadata || {}
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
if (updates.content.content !== void 0) {
|
|
1593
|
+
newContent.content = updates.content.content;
|
|
1594
|
+
}
|
|
1595
|
+
if ("parts" in updates.content && updates.content.parts !== void 0) {
|
|
1596
|
+
newContent.parts = updates.content.parts;
|
|
1597
|
+
}
|
|
1598
|
+
updatePayload.content = JSON.stringify(newContent);
|
|
1599
|
+
}
|
|
1600
|
+
await this.service.entities.message.update({ entity: "message", id }).set(updatePayload).go();
|
|
1601
|
+
const updatedMessage = await this.service.entities.message.get({ entity: "message", id }).go();
|
|
1602
|
+
if (updatedMessage.data) {
|
|
1603
|
+
updatedMessages.push(this.parseMessageData(updatedMessage.data));
|
|
1604
|
+
}
|
|
602
1605
|
}
|
|
603
|
-
|
|
1606
|
+
for (const threadId of affectedThreadIds) {
|
|
1607
|
+
await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
|
|
1608
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1609
|
+
}).go();
|
|
1610
|
+
}
|
|
1611
|
+
return updatedMessages;
|
|
604
1612
|
} catch (error) {
|
|
605
|
-
|
|
606
|
-
|
|
1613
|
+
throw new MastraError(
|
|
1614
|
+
{
|
|
1615
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_MESSAGES_FAILED",
|
|
1616
|
+
domain: ErrorDomain.STORAGE,
|
|
1617
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1618
|
+
details: { count: messages.length }
|
|
1619
|
+
},
|
|
1620
|
+
error
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
async getResourceById({ resourceId }) {
|
|
1625
|
+
this.logger.debug("Getting resource by ID", { resourceId });
|
|
1626
|
+
try {
|
|
1627
|
+
const result = await this.service.entities.resource.get({ entity: "resource", id: resourceId }).go();
|
|
1628
|
+
if (!result.data) {
|
|
1629
|
+
return null;
|
|
1630
|
+
}
|
|
1631
|
+
const data = result.data;
|
|
1632
|
+
return {
|
|
1633
|
+
...data,
|
|
1634
|
+
// Convert date strings back to Date objects for consistency
|
|
1635
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
1636
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt,
|
|
1637
|
+
// Ensure workingMemory is always returned as a string, regardless of automatic parsing
|
|
1638
|
+
workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory
|
|
1639
|
+
// metadata is already transformed by the entity's getter
|
|
1640
|
+
};
|
|
1641
|
+
} catch (error) {
|
|
1642
|
+
throw new MastraError(
|
|
1643
|
+
{
|
|
1644
|
+
id: "STORAGE_DYNAMODB_STORE_GET_RESOURCE_BY_ID_FAILED",
|
|
1645
|
+
domain: ErrorDomain.STORAGE,
|
|
1646
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1647
|
+
details: { resourceId }
|
|
1648
|
+
},
|
|
1649
|
+
error
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
async saveResource({ resource }) {
|
|
1654
|
+
this.logger.debug("Saving resource", { resourceId: resource.id });
|
|
1655
|
+
const now = /* @__PURE__ */ new Date();
|
|
1656
|
+
const resourceData = {
|
|
1657
|
+
entity: "resource",
|
|
1658
|
+
id: resource.id,
|
|
1659
|
+
workingMemory: resource.workingMemory,
|
|
1660
|
+
metadata: resource.metadata ? JSON.stringify(resource.metadata) : void 0,
|
|
1661
|
+
createdAt: resource.createdAt?.toISOString() || now.toISOString(),
|
|
1662
|
+
updatedAt: now.toISOString()
|
|
1663
|
+
};
|
|
1664
|
+
try {
|
|
1665
|
+
await this.service.entities.resource.upsert(resourceData).go();
|
|
1666
|
+
return {
|
|
1667
|
+
id: resource.id,
|
|
1668
|
+
workingMemory: resource.workingMemory,
|
|
1669
|
+
metadata: resource.metadata,
|
|
1670
|
+
createdAt: resource.createdAt || now,
|
|
1671
|
+
updatedAt: now
|
|
1672
|
+
};
|
|
1673
|
+
} catch (error) {
|
|
1674
|
+
throw new MastraError(
|
|
1675
|
+
{
|
|
1676
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_RESOURCE_FAILED",
|
|
1677
|
+
domain: ErrorDomain.STORAGE,
|
|
1678
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1679
|
+
details: { resourceId: resource.id }
|
|
1680
|
+
},
|
|
1681
|
+
error
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
async updateResource({
|
|
1686
|
+
resourceId,
|
|
1687
|
+
workingMemory,
|
|
1688
|
+
metadata
|
|
1689
|
+
}) {
|
|
1690
|
+
this.logger.debug("Updating resource", { resourceId });
|
|
1691
|
+
try {
|
|
1692
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1693
|
+
if (!existingResource) {
|
|
1694
|
+
const newResource = {
|
|
1695
|
+
id: resourceId,
|
|
1696
|
+
workingMemory,
|
|
1697
|
+
metadata: metadata || {},
|
|
1698
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1699
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1700
|
+
};
|
|
1701
|
+
return this.saveResource({ resource: newResource });
|
|
1702
|
+
}
|
|
1703
|
+
const now = /* @__PURE__ */ new Date();
|
|
1704
|
+
const updateData = {
|
|
1705
|
+
updatedAt: now.toISOString()
|
|
1706
|
+
};
|
|
1707
|
+
if (workingMemory !== void 0) {
|
|
1708
|
+
updateData.workingMemory = workingMemory;
|
|
1709
|
+
}
|
|
1710
|
+
if (metadata) {
|
|
1711
|
+
const existingMetadata = existingResource.metadata || {};
|
|
1712
|
+
const mergedMetadata = { ...existingMetadata, ...metadata };
|
|
1713
|
+
updateData.metadata = JSON.stringify(mergedMetadata);
|
|
1714
|
+
}
|
|
1715
|
+
await this.service.entities.resource.update({ entity: "resource", id: resourceId }).set(updateData).go();
|
|
1716
|
+
return {
|
|
1717
|
+
...existingResource,
|
|
1718
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1719
|
+
metadata: metadata ? { ...existingResource.metadata, ...metadata } : existingResource.metadata,
|
|
1720
|
+
updatedAt: now
|
|
1721
|
+
};
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
throw new MastraError(
|
|
1724
|
+
{
|
|
1725
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_RESOURCE_FAILED",
|
|
1726
|
+
domain: ErrorDomain.STORAGE,
|
|
1727
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1728
|
+
details: { resourceId }
|
|
1729
|
+
},
|
|
1730
|
+
error
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
};
|
|
1735
|
+
var StoreOperationsDynamoDB = class extends StoreOperations {
|
|
1736
|
+
client;
|
|
1737
|
+
tableName;
|
|
1738
|
+
service;
|
|
1739
|
+
constructor({
|
|
1740
|
+
service,
|
|
1741
|
+
tableName,
|
|
1742
|
+
client
|
|
1743
|
+
}) {
|
|
1744
|
+
super();
|
|
1745
|
+
this.service = service;
|
|
1746
|
+
this.client = client;
|
|
1747
|
+
this.tableName = tableName;
|
|
1748
|
+
}
|
|
1749
|
+
async hasColumn() {
|
|
1750
|
+
return true;
|
|
1751
|
+
}
|
|
1752
|
+
async dropTable() {
|
|
1753
|
+
}
|
|
1754
|
+
// Helper methods for entity/table mapping
|
|
1755
|
+
getEntityNameForTable(tableName) {
|
|
1756
|
+
const mapping = {
|
|
1757
|
+
[TABLE_THREADS]: "thread",
|
|
1758
|
+
[TABLE_MESSAGES]: "message",
|
|
1759
|
+
[TABLE_WORKFLOW_SNAPSHOT]: "workflow_snapshot",
|
|
1760
|
+
[TABLE_EVALS]: "eval",
|
|
1761
|
+
[TABLE_SCORERS]: "score",
|
|
1762
|
+
[TABLE_TRACES]: "trace",
|
|
1763
|
+
[TABLE_RESOURCES]: "resource"
|
|
1764
|
+
};
|
|
1765
|
+
return mapping[tableName] || null;
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* Pre-processes a record to ensure Date objects are converted to ISO strings
|
|
1769
|
+
* This is necessary because ElectroDB validation happens before setters are applied
|
|
1770
|
+
*/
|
|
1771
|
+
preprocessRecord(record) {
|
|
1772
|
+
const processed = { ...record };
|
|
1773
|
+
if (processed.createdAt instanceof Date) {
|
|
1774
|
+
processed.createdAt = processed.createdAt.toISOString();
|
|
1775
|
+
}
|
|
1776
|
+
if (processed.updatedAt instanceof Date) {
|
|
1777
|
+
processed.updatedAt = processed.updatedAt.toISOString();
|
|
1778
|
+
}
|
|
1779
|
+
if (processed.created_at instanceof Date) {
|
|
1780
|
+
processed.created_at = processed.created_at.toISOString();
|
|
1781
|
+
}
|
|
1782
|
+
if (processed.result && typeof processed.result === "object") {
|
|
1783
|
+
processed.result = JSON.stringify(processed.result);
|
|
1784
|
+
}
|
|
1785
|
+
if (processed.test_info && typeof processed.test_info === "object") {
|
|
1786
|
+
processed.test_info = JSON.stringify(processed.test_info);
|
|
1787
|
+
} else if (processed.test_info === void 0 || processed.test_info === null) {
|
|
1788
|
+
delete processed.test_info;
|
|
1789
|
+
}
|
|
1790
|
+
if (processed.snapshot && typeof processed.snapshot === "object") {
|
|
1791
|
+
processed.snapshot = JSON.stringify(processed.snapshot);
|
|
1792
|
+
}
|
|
1793
|
+
if (processed.attributes && typeof processed.attributes === "object") {
|
|
1794
|
+
processed.attributes = JSON.stringify(processed.attributes);
|
|
607
1795
|
}
|
|
1796
|
+
if (processed.status && typeof processed.status === "object") {
|
|
1797
|
+
processed.status = JSON.stringify(processed.status);
|
|
1798
|
+
}
|
|
1799
|
+
if (processed.events && typeof processed.events === "object") {
|
|
1800
|
+
processed.events = JSON.stringify(processed.events);
|
|
1801
|
+
}
|
|
1802
|
+
if (processed.links && typeof processed.links === "object") {
|
|
1803
|
+
processed.links = JSON.stringify(processed.links);
|
|
1804
|
+
}
|
|
1805
|
+
return processed;
|
|
608
1806
|
}
|
|
609
1807
|
/**
|
|
610
1808
|
* Validates that the required DynamoDB table exists and is accessible.
|
|
@@ -622,57 +1820,76 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
622
1820
|
if (error.name === "ResourceNotFoundException") {
|
|
623
1821
|
return false;
|
|
624
1822
|
}
|
|
625
|
-
throw
|
|
1823
|
+
throw new MastraError(
|
|
1824
|
+
{
|
|
1825
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
|
|
1826
|
+
domain: ErrorDomain.STORAGE,
|
|
1827
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1828
|
+
details: { tableName: this.tableName }
|
|
1829
|
+
},
|
|
1830
|
+
error
|
|
1831
|
+
);
|
|
626
1832
|
}
|
|
627
1833
|
}
|
|
628
1834
|
/**
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
1835
|
+
* This method is modified for DynamoDB with ElectroDB single-table design.
|
|
1836
|
+
* It assumes the table is created and managed externally via CDK/CloudFormation.
|
|
1837
|
+
*
|
|
1838
|
+
* This implementation only validates that the required table exists and is accessible.
|
|
1839
|
+
* No table creation is attempted - we simply check if we can access the table.
|
|
632
1840
|
*/
|
|
633
|
-
async
|
|
634
|
-
|
|
635
|
-
this.hasInitialized = this._performInitializationAndStore();
|
|
636
|
-
}
|
|
1841
|
+
async createTable({ tableName }) {
|
|
1842
|
+
this.logger.debug("Validating access to externally managed table", { tableName, physicalTable: this.tableName });
|
|
637
1843
|
try {
|
|
638
|
-
await this.
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* Pre-processes a record to ensure Date objects are converted to ISO strings
|
|
662
|
-
* This is necessary because ElectroDB validation happens before setters are applied
|
|
663
|
-
*/
|
|
664
|
-
preprocessRecord(record) {
|
|
665
|
-
const processed = { ...record };
|
|
666
|
-
if (processed.createdAt instanceof Date) {
|
|
667
|
-
processed.createdAt = processed.createdAt.toISOString();
|
|
1844
|
+
const tableExists = await this.validateTableExists();
|
|
1845
|
+
if (!tableExists) {
|
|
1846
|
+
this.logger.error(
|
|
1847
|
+
`Table ${this.tableName} does not exist or is not accessible. It should be created via CDK/CloudFormation.`
|
|
1848
|
+
);
|
|
1849
|
+
throw new Error(
|
|
1850
|
+
`Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
this.logger.debug(`Table ${this.tableName} exists and is accessible`);
|
|
1854
|
+
} catch (error) {
|
|
1855
|
+
this.logger.error("Error validating table access", { tableName: this.tableName, error });
|
|
1856
|
+
throw new MastraError(
|
|
1857
|
+
{
|
|
1858
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED",
|
|
1859
|
+
domain: ErrorDomain.STORAGE,
|
|
1860
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1861
|
+
details: { tableName: this.tableName }
|
|
1862
|
+
},
|
|
1863
|
+
error
|
|
1864
|
+
);
|
|
668
1865
|
}
|
|
669
|
-
|
|
670
|
-
|
|
1866
|
+
}
|
|
1867
|
+
async insert({ tableName, record }) {
|
|
1868
|
+
this.logger.debug("DynamoDB insert called", { tableName });
|
|
1869
|
+
const entityName = this.getEntityNameForTable(tableName);
|
|
1870
|
+
if (!entityName || !this.service.entities[entityName]) {
|
|
1871
|
+
throw new MastraError({
|
|
1872
|
+
id: "STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS",
|
|
1873
|
+
domain: ErrorDomain.STORAGE,
|
|
1874
|
+
category: ErrorCategory.USER,
|
|
1875
|
+
text: "No entity defined for tableName",
|
|
1876
|
+
details: { tableName }
|
|
1877
|
+
});
|
|
671
1878
|
}
|
|
672
|
-
|
|
673
|
-
|
|
1879
|
+
try {
|
|
1880
|
+
const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
|
|
1881
|
+
await this.service.entities[entityName].create(dataToSave).go();
|
|
1882
|
+
} catch (error) {
|
|
1883
|
+
throw new MastraError(
|
|
1884
|
+
{
|
|
1885
|
+
id: "STORAGE_DYNAMODB_STORE_INSERT_FAILED",
|
|
1886
|
+
domain: ErrorDomain.STORAGE,
|
|
1887
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1888
|
+
details: { tableName }
|
|
1889
|
+
},
|
|
1890
|
+
error
|
|
1891
|
+
);
|
|
674
1892
|
}
|
|
675
|
-
return processed;
|
|
676
1893
|
}
|
|
677
1894
|
async alterTable(_args) {
|
|
678
1895
|
}
|
|
@@ -683,7 +1900,13 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
683
1900
|
this.logger.debug("DynamoDB clearTable called", { tableName });
|
|
684
1901
|
const entityName = this.getEntityNameForTable(tableName);
|
|
685
1902
|
if (!entityName || !this.service.entities[entityName]) {
|
|
686
|
-
throw new
|
|
1903
|
+
throw new MastraError({
|
|
1904
|
+
id: "STORAGE_DYNAMODB_STORE_CLEAR_TABLE_INVALID_ARGS",
|
|
1905
|
+
domain: ErrorDomain.STORAGE,
|
|
1906
|
+
category: ErrorCategory.USER,
|
|
1907
|
+
text: "No entity defined for tableName",
|
|
1908
|
+
details: { tableName }
|
|
1909
|
+
});
|
|
687
1910
|
}
|
|
688
1911
|
try {
|
|
689
1912
|
const result = await this.service.entities[entityName].scan.go({ pages: "all" });
|
|
@@ -703,10 +1926,10 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
703
1926
|
if (!item.id) throw new Error(`Missing required key 'id' for entity 'message'`);
|
|
704
1927
|
key.id = item.id;
|
|
705
1928
|
break;
|
|
706
|
-
case "
|
|
1929
|
+
case "workflow_snapshot":
|
|
707
1930
|
if (!item.workflow_name)
|
|
708
|
-
throw new Error(`Missing required key 'workflow_name' for entity '
|
|
709
|
-
if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity '
|
|
1931
|
+
throw new Error(`Missing required key 'workflow_name' for entity 'workflow_snapshot'`);
|
|
1932
|
+
if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflow_snapshot'`);
|
|
710
1933
|
key.workflow_name = item.workflow_name;
|
|
711
1934
|
key.run_id = item.run_id;
|
|
712
1935
|
break;
|
|
@@ -718,6 +1941,10 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
718
1941
|
if (!item.id) throw new Error(`Missing required key 'id' for entity 'trace'`);
|
|
719
1942
|
key.id = item.id;
|
|
720
1943
|
break;
|
|
1944
|
+
case "score":
|
|
1945
|
+
if (!item.id) throw new Error(`Missing required key 'id' for entity 'score'`);
|
|
1946
|
+
key.id = item.id;
|
|
1947
|
+
break;
|
|
721
1948
|
default:
|
|
722
1949
|
this.logger.warn(`Unknown entity type encountered during clearTable: ${entityName}`);
|
|
723
1950
|
throw new Error(`Cannot construct delete key for unknown entity type: ${entityName}`);
|
|
@@ -731,25 +1958,15 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
731
1958
|
}
|
|
732
1959
|
this.logger.debug(`Successfully cleared all records for ${tableName}`);
|
|
733
1960
|
} catch (error) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
const entityName = this.getEntityNameForTable(tableName);
|
|
744
|
-
if (!entityName || !this.service.entities[entityName]) {
|
|
745
|
-
throw new Error(`No entity defined for ${tableName}`);
|
|
746
|
-
}
|
|
747
|
-
try {
|
|
748
|
-
const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
|
|
749
|
-
await this.service.entities[entityName].create(dataToSave).go();
|
|
750
|
-
} catch (error) {
|
|
751
|
-
this.logger.error("Failed to insert record", { tableName, error });
|
|
752
|
-
throw error;
|
|
1961
|
+
throw new MastraError(
|
|
1962
|
+
{
|
|
1963
|
+
id: "STORAGE_DYNAMODB_STORE_CLEAR_TABLE_FAILED",
|
|
1964
|
+
domain: ErrorDomain.STORAGE,
|
|
1965
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1966
|
+
details: { tableName }
|
|
1967
|
+
},
|
|
1968
|
+
error
|
|
1969
|
+
);
|
|
753
1970
|
}
|
|
754
1971
|
}
|
|
755
1972
|
/**
|
|
@@ -759,7 +1976,13 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
759
1976
|
this.logger.debug("DynamoDB batchInsert called", { tableName, count: records.length });
|
|
760
1977
|
const entityName = this.getEntityNameForTable(tableName);
|
|
761
1978
|
if (!entityName || !this.service.entities[entityName]) {
|
|
762
|
-
throw new
|
|
1979
|
+
throw new MastraError({
|
|
1980
|
+
id: "STORAGE_DYNAMODB_STORE_BATCH_INSERT_INVALID_ARGS",
|
|
1981
|
+
domain: ErrorDomain.STORAGE,
|
|
1982
|
+
category: ErrorCategory.USER,
|
|
1983
|
+
text: "No entity defined for tableName",
|
|
1984
|
+
details: { tableName }
|
|
1985
|
+
});
|
|
763
1986
|
}
|
|
764
1987
|
const recordsToSave = records.map((rec) => ({ entity: entityName, ...this.preprocessRecord(rec) }));
|
|
765
1988
|
const batchSize = 25;
|
|
@@ -780,8 +2003,15 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
780
2003
|
}
|
|
781
2004
|
}
|
|
782
2005
|
} catch (error) {
|
|
783
|
-
|
|
784
|
-
|
|
2006
|
+
throw new MastraError(
|
|
2007
|
+
{
|
|
2008
|
+
id: "STORAGE_DYNAMODB_STORE_BATCH_INSERT_FAILED",
|
|
2009
|
+
domain: ErrorDomain.STORAGE,
|
|
2010
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2011
|
+
details: { tableName }
|
|
2012
|
+
},
|
|
2013
|
+
error
|
|
2014
|
+
);
|
|
785
2015
|
}
|
|
786
2016
|
}
|
|
787
2017
|
/**
|
|
@@ -791,7 +2021,13 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
791
2021
|
this.logger.debug("DynamoDB load called", { tableName, keys });
|
|
792
2022
|
const entityName = this.getEntityNameForTable(tableName);
|
|
793
2023
|
if (!entityName || !this.service.entities[entityName]) {
|
|
794
|
-
throw new
|
|
2024
|
+
throw new MastraError({
|
|
2025
|
+
id: "STORAGE_DYNAMODB_STORE_LOAD_INVALID_ARGS",
|
|
2026
|
+
domain: ErrorDomain.STORAGE,
|
|
2027
|
+
category: ErrorCategory.USER,
|
|
2028
|
+
text: "No entity defined for tableName",
|
|
2029
|
+
details: { tableName }
|
|
2030
|
+
});
|
|
795
2031
|
}
|
|
796
2032
|
try {
|
|
797
2033
|
const keyObject = { entity: entityName, ...keys };
|
|
@@ -802,223 +2038,238 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
802
2038
|
let data = result.data;
|
|
803
2039
|
return data;
|
|
804
2040
|
} catch (error) {
|
|
805
|
-
|
|
806
|
-
|
|
2041
|
+
throw new MastraError(
|
|
2042
|
+
{
|
|
2043
|
+
id: "STORAGE_DYNAMODB_STORE_LOAD_FAILED",
|
|
2044
|
+
domain: ErrorDomain.STORAGE,
|
|
2045
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2046
|
+
details: { tableName }
|
|
2047
|
+
},
|
|
2048
|
+
error
|
|
2049
|
+
);
|
|
807
2050
|
}
|
|
808
2051
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
2052
|
+
};
|
|
2053
|
+
var ScoresStorageDynamoDB = class extends ScoresStorage {
|
|
2054
|
+
service;
|
|
2055
|
+
constructor({ service }) {
|
|
2056
|
+
super();
|
|
2057
|
+
this.service = service;
|
|
2058
|
+
}
|
|
2059
|
+
// Helper function to parse score data (handle JSON fields)
|
|
2060
|
+
parseScoreData(data) {
|
|
2061
|
+
return {
|
|
2062
|
+
...data,
|
|
2063
|
+
// Convert date strings back to Date objects for consistency
|
|
2064
|
+
createdAt: data.createdAt ? new Date(data.createdAt) : /* @__PURE__ */ new Date(),
|
|
2065
|
+
updatedAt: data.updatedAt ? new Date(data.updatedAt) : /* @__PURE__ */ new Date()
|
|
2066
|
+
// JSON fields are already transformed by the entity's getters
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
async getScoreById({ id }) {
|
|
2070
|
+
this.logger.debug("Getting score by ID", { id });
|
|
812
2071
|
try {
|
|
813
|
-
const result = await this.service.entities.
|
|
2072
|
+
const result = await this.service.entities.score.get({ entity: "score", id }).go();
|
|
814
2073
|
if (!result.data) {
|
|
815
2074
|
return null;
|
|
816
2075
|
}
|
|
817
|
-
|
|
818
|
-
return {
|
|
819
|
-
...data,
|
|
820
|
-
// Convert date strings back to Date objects for consistency
|
|
821
|
-
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
822
|
-
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
823
|
-
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
824
|
-
// metadata is already transformed by the entity's getter
|
|
825
|
-
};
|
|
826
|
-
} catch (error) {
|
|
827
|
-
this.logger.error("Failed to get thread by ID", { threadId, error });
|
|
828
|
-
throw error;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
async getThreadsByResourceId({ resourceId }) {
|
|
832
|
-
this.logger.debug("Getting threads by resource ID", { resourceId });
|
|
833
|
-
try {
|
|
834
|
-
const result = await this.service.entities.thread.query.byResource({ entity: "thread", resourceId }).go();
|
|
835
|
-
if (!result.data.length) {
|
|
836
|
-
return [];
|
|
837
|
-
}
|
|
838
|
-
return result.data.map((data) => ({
|
|
839
|
-
...data,
|
|
840
|
-
// Convert date strings back to Date objects for consistency
|
|
841
|
-
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
842
|
-
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
843
|
-
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
844
|
-
// metadata is already transformed by the entity's getter
|
|
845
|
-
}));
|
|
2076
|
+
return this.parseScoreData(result.data);
|
|
846
2077
|
} catch (error) {
|
|
847
|
-
|
|
848
|
-
|
|
2078
|
+
throw new MastraError(
|
|
2079
|
+
{
|
|
2080
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORE_BY_ID_FAILED",
|
|
2081
|
+
domain: ErrorDomain.STORAGE,
|
|
2082
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2083
|
+
details: { id }
|
|
2084
|
+
},
|
|
2085
|
+
error
|
|
2086
|
+
);
|
|
849
2087
|
}
|
|
850
2088
|
}
|
|
851
|
-
async
|
|
852
|
-
this.logger.debug("Saving
|
|
2089
|
+
async saveScore(score) {
|
|
2090
|
+
this.logger.debug("Saving score", { scorerId: score.scorerId, runId: score.runId });
|
|
853
2091
|
const now = /* @__PURE__ */ new Date();
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
2092
|
+
const scoreId = `score-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
2093
|
+
const scoreData = {
|
|
2094
|
+
entity: "score",
|
|
2095
|
+
id: scoreId,
|
|
2096
|
+
scorerId: score.scorerId,
|
|
2097
|
+
traceId: score.traceId || "",
|
|
2098
|
+
runId: score.runId,
|
|
2099
|
+
scorer: typeof score.scorer === "string" ? score.scorer : JSON.stringify(score.scorer),
|
|
2100
|
+
extractStepResult: typeof score.extractStepResult === "string" ? score.extractStepResult : JSON.stringify(score.extractStepResult),
|
|
2101
|
+
analyzeStepResult: typeof score.analyzeStepResult === "string" ? score.analyzeStepResult : JSON.stringify(score.analyzeStepResult),
|
|
2102
|
+
score: score.score,
|
|
2103
|
+
reason: score.reason,
|
|
2104
|
+
extractPrompt: score.extractPrompt,
|
|
2105
|
+
analyzePrompt: score.analyzePrompt,
|
|
2106
|
+
reasonPrompt: score.reasonPrompt,
|
|
2107
|
+
input: typeof score.input === "string" ? score.input : JSON.stringify(score.input),
|
|
2108
|
+
output: typeof score.output === "string" ? score.output : JSON.stringify(score.output),
|
|
2109
|
+
additionalContext: typeof score.additionalContext === "string" ? score.additionalContext : JSON.stringify(score.additionalContext),
|
|
2110
|
+
runtimeContext: typeof score.runtimeContext === "string" ? score.runtimeContext : JSON.stringify(score.runtimeContext),
|
|
2111
|
+
entityType: score.entityType,
|
|
2112
|
+
entityData: typeof score.entity === "string" ? score.entity : JSON.stringify(score.entity),
|
|
2113
|
+
entityId: score.entityId,
|
|
2114
|
+
source: score.source,
|
|
2115
|
+
resourceId: score.resourceId || "",
|
|
2116
|
+
threadId: score.threadId || "",
|
|
2117
|
+
createdAt: now.toISOString(),
|
|
2118
|
+
updatedAt: now.toISOString()
|
|
862
2119
|
};
|
|
863
2120
|
try {
|
|
864
|
-
await this.service.entities.
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
updatedAt: now,
|
|
871
|
-
metadata: thread.metadata
|
|
2121
|
+
await this.service.entities.score.upsert(scoreData).go();
|
|
2122
|
+
const savedScore = {
|
|
2123
|
+
...score,
|
|
2124
|
+
id: scoreId,
|
|
2125
|
+
createdAt: now,
|
|
2126
|
+
updatedAt: now
|
|
872
2127
|
};
|
|
2128
|
+
return { score: savedScore };
|
|
873
2129
|
} catch (error) {
|
|
874
|
-
|
|
875
|
-
|
|
2130
|
+
throw new MastraError(
|
|
2131
|
+
{
|
|
2132
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
|
|
2133
|
+
domain: ErrorDomain.STORAGE,
|
|
2134
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2135
|
+
details: { scorerId: score.scorerId, runId: score.runId }
|
|
2136
|
+
},
|
|
2137
|
+
error
|
|
2138
|
+
);
|
|
876
2139
|
}
|
|
877
2140
|
}
|
|
878
|
-
async
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
2141
|
+
async getScoresByScorerId({
|
|
2142
|
+
scorerId,
|
|
2143
|
+
pagination,
|
|
2144
|
+
entityId,
|
|
2145
|
+
entityType
|
|
882
2146
|
}) {
|
|
883
|
-
this.logger.debug("
|
|
2147
|
+
this.logger.debug("Getting scores by scorer ID", { scorerId, pagination, entityId, entityType });
|
|
884
2148
|
try {
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
const updateData = {
|
|
891
|
-
updatedAt: now.toISOString()
|
|
892
|
-
};
|
|
893
|
-
if (title) {
|
|
894
|
-
updateData.title = title;
|
|
2149
|
+
const query = this.service.entities.score.query.byScorer({ entity: "score", scorerId });
|
|
2150
|
+
const results = await query.go();
|
|
2151
|
+
let allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2152
|
+
if (entityId) {
|
|
2153
|
+
allScores = allScores.filter((score) => score.entityId === entityId);
|
|
895
2154
|
}
|
|
896
|
-
if (
|
|
897
|
-
|
|
2155
|
+
if (entityType) {
|
|
2156
|
+
allScores = allScores.filter((score) => score.entityType === entityType);
|
|
898
2157
|
}
|
|
899
|
-
|
|
2158
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2159
|
+
const startIndex = pagination.page * pagination.perPage;
|
|
2160
|
+
const endIndex = startIndex + pagination.perPage;
|
|
2161
|
+
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
2162
|
+
const total = allScores.length;
|
|
2163
|
+
const hasMore = endIndex < total;
|
|
900
2164
|
return {
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
2165
|
+
scores: paginatedScores,
|
|
2166
|
+
pagination: {
|
|
2167
|
+
total,
|
|
2168
|
+
page: pagination.page,
|
|
2169
|
+
perPage: pagination.perPage,
|
|
2170
|
+
hasMore
|
|
2171
|
+
}
|
|
905
2172
|
};
|
|
906
2173
|
} catch (error) {
|
|
907
|
-
|
|
908
|
-
|
|
2174
|
+
throw new MastraError(
|
|
2175
|
+
{
|
|
2176
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
|
|
2177
|
+
domain: ErrorDomain.STORAGE,
|
|
2178
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2179
|
+
details: {
|
|
2180
|
+
scorerId: scorerId || "",
|
|
2181
|
+
entityId: entityId || "",
|
|
2182
|
+
entityType: entityType || "",
|
|
2183
|
+
page: pagination.page,
|
|
2184
|
+
perPage: pagination.perPage
|
|
2185
|
+
}
|
|
2186
|
+
},
|
|
2187
|
+
error
|
|
2188
|
+
);
|
|
909
2189
|
}
|
|
910
2190
|
}
|
|
911
|
-
async
|
|
912
|
-
|
|
2191
|
+
async getScoresByRunId({
|
|
2192
|
+
runId,
|
|
2193
|
+
pagination
|
|
2194
|
+
}) {
|
|
2195
|
+
this.logger.debug("Getting scores by run ID", { runId, pagination });
|
|
913
2196
|
try {
|
|
914
|
-
|
|
2197
|
+
const query = this.service.entities.score.query.byRun({ entity: "score", runId });
|
|
2198
|
+
const results = await query.go();
|
|
2199
|
+
const allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2200
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2201
|
+
const startIndex = pagination.page * pagination.perPage;
|
|
2202
|
+
const endIndex = startIndex + pagination.perPage;
|
|
2203
|
+
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
2204
|
+
const total = allScores.length;
|
|
2205
|
+
const hasMore = endIndex < total;
|
|
2206
|
+
return {
|
|
2207
|
+
scores: paginatedScores,
|
|
2208
|
+
pagination: {
|
|
2209
|
+
total,
|
|
2210
|
+
page: pagination.page,
|
|
2211
|
+
perPage: pagination.perPage,
|
|
2212
|
+
hasMore
|
|
2213
|
+
}
|
|
2214
|
+
};
|
|
915
2215
|
} catch (error) {
|
|
916
|
-
|
|
917
|
-
|
|
2216
|
+
throw new MastraError(
|
|
2217
|
+
{
|
|
2218
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_RUN_ID_FAILED",
|
|
2219
|
+
domain: ErrorDomain.STORAGE,
|
|
2220
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2221
|
+
details: { runId, page: pagination.page, perPage: pagination.perPage }
|
|
2222
|
+
},
|
|
2223
|
+
error
|
|
2224
|
+
);
|
|
918
2225
|
}
|
|
919
2226
|
}
|
|
920
|
-
async
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
format
|
|
2227
|
+
async getScoresByEntityId({
|
|
2228
|
+
entityId,
|
|
2229
|
+
entityType,
|
|
2230
|
+
pagination
|
|
925
2231
|
}) {
|
|
926
|
-
this.logger.debug("Getting
|
|
2232
|
+
this.logger.debug("Getting scores by entity ID", { entityId, entityType, pagination });
|
|
927
2233
|
try {
|
|
928
|
-
const query = this.service.entities.
|
|
929
|
-
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
930
|
-
if (limit !== Number.MAX_SAFE_INTEGER) {
|
|
931
|
-
const results2 = await query.go({ limit, order: "desc" });
|
|
932
|
-
const list2 = new MessageList({ threadId, resourceId }).add(
|
|
933
|
-
results2.data.map((data) => this.parseMessageData(data)),
|
|
934
|
-
"memory"
|
|
935
|
-
);
|
|
936
|
-
if (format === `v2`) return list2.get.all.v2();
|
|
937
|
-
return list2.get.all.v1();
|
|
938
|
-
}
|
|
2234
|
+
const query = this.service.entities.score.query.byEntityData({ entity: "score", entityId });
|
|
939
2235
|
const results = await query.go();
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
throw error;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
async saveMessages(args) {
|
|
952
|
-
const { messages, format = "v1" } = args;
|
|
953
|
-
this.logger.debug("Saving messages", { count: messages.length });
|
|
954
|
-
if (!messages.length) {
|
|
955
|
-
return [];
|
|
956
|
-
}
|
|
957
|
-
const threadId = messages[0]?.threadId;
|
|
958
|
-
if (!threadId) {
|
|
959
|
-
throw new Error("Thread ID is required");
|
|
960
|
-
}
|
|
961
|
-
const messagesToSave = messages.map((msg) => {
|
|
962
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2236
|
+
let allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2237
|
+
allScores = allScores.filter((score) => score.entityType === entityType);
|
|
2238
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2239
|
+
const startIndex = pagination.page * pagination.perPage;
|
|
2240
|
+
const endIndex = startIndex + pagination.perPage;
|
|
2241
|
+
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
2242
|
+
const total = allScores.length;
|
|
2243
|
+
const hasMore = endIndex < total;
|
|
963
2244
|
return {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
// Ensure complex fields are stringified if not handled by attribute setters
|
|
972
|
-
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
973
|
-
toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
|
|
974
|
-
toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
|
|
975
|
-
toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
|
|
976
|
-
createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
|
|
977
|
-
updatedAt: now
|
|
978
|
-
// Add updatedAt
|
|
2245
|
+
scores: paginatedScores,
|
|
2246
|
+
pagination: {
|
|
2247
|
+
total,
|
|
2248
|
+
page: pagination.page,
|
|
2249
|
+
perPage: pagination.perPage,
|
|
2250
|
+
hasMore
|
|
2251
|
+
}
|
|
979
2252
|
};
|
|
980
|
-
});
|
|
981
|
-
try {
|
|
982
|
-
const batchSize = 25;
|
|
983
|
-
const batches = [];
|
|
984
|
-
for (let i = 0; i < messagesToSave.length; i += batchSize) {
|
|
985
|
-
const batch = messagesToSave.slice(i, i + batchSize);
|
|
986
|
-
batches.push(batch);
|
|
987
|
-
}
|
|
988
|
-
await Promise.all([
|
|
989
|
-
// Process message batches
|
|
990
|
-
...batches.map(async (batch) => {
|
|
991
|
-
for (const messageData of batch) {
|
|
992
|
-
if (!messageData.entity) {
|
|
993
|
-
this.logger.error("Missing entity property in message data for create", { messageData });
|
|
994
|
-
throw new Error("Internal error: Missing entity property during saveMessages");
|
|
995
|
-
}
|
|
996
|
-
await this.service.entities.message.create(messageData).go();
|
|
997
|
-
}
|
|
998
|
-
}),
|
|
999
|
-
// Update thread's updatedAt timestamp
|
|
1000
|
-
this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
|
|
1001
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1002
|
-
}).go()
|
|
1003
|
-
]);
|
|
1004
|
-
const list = new MessageList().add(messages, "memory");
|
|
1005
|
-
if (format === `v1`) return list.get.all.v1();
|
|
1006
|
-
return list.get.all.v2();
|
|
1007
2253
|
} catch (error) {
|
|
1008
|
-
|
|
1009
|
-
|
|
2254
|
+
throw new MastraError(
|
|
2255
|
+
{
|
|
2256
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
2257
|
+
domain: ErrorDomain.STORAGE,
|
|
2258
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2259
|
+
details: { entityId, entityType, page: pagination.page, perPage: pagination.perPage }
|
|
2260
|
+
},
|
|
2261
|
+
error
|
|
2262
|
+
);
|
|
1010
2263
|
}
|
|
1011
2264
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
// transformed by the ElectroDB entity getters.
|
|
1021
|
-
};
|
|
2265
|
+
};
|
|
2266
|
+
var TracesStorageDynamoDB = class extends TracesStorage {
|
|
2267
|
+
service;
|
|
2268
|
+
operations;
|
|
2269
|
+
constructor({ service, operations }) {
|
|
2270
|
+
super();
|
|
2271
|
+
this.service = service;
|
|
2272
|
+
this.operations = operations;
|
|
1022
2273
|
}
|
|
1023
2274
|
// Trace operations
|
|
1024
2275
|
async getTraces(args) {
|
|
@@ -1045,34 +2296,222 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1045
2296
|
items = results.data;
|
|
1046
2297
|
break;
|
|
1047
2298
|
}
|
|
1048
|
-
cursor = results.cursor;
|
|
1049
|
-
if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
|
|
1050
|
-
break;
|
|
2299
|
+
cursor = results.cursor;
|
|
2300
|
+
if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
|
|
2301
|
+
break;
|
|
2302
|
+
}
|
|
2303
|
+
} while (cursor && pagesFetched < startPage);
|
|
2304
|
+
return items;
|
|
2305
|
+
} catch (error) {
|
|
2306
|
+
throw new MastraError(
|
|
2307
|
+
{
|
|
2308
|
+
id: "STORAGE_DYNAMODB_STORE_GET_TRACES_FAILED",
|
|
2309
|
+
domain: ErrorDomain.STORAGE,
|
|
2310
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2311
|
+
},
|
|
2312
|
+
error
|
|
2313
|
+
);
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
async batchTraceInsert({ records }) {
|
|
2317
|
+
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
2318
|
+
if (!records.length) {
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
try {
|
|
2322
|
+
const recordsToSave = records.map((rec) => ({ entity: "trace", ...rec }));
|
|
2323
|
+
await this.operations.batchInsert({
|
|
2324
|
+
tableName: TABLE_TRACES,
|
|
2325
|
+
records: recordsToSave
|
|
2326
|
+
// Pass records with 'entity' included
|
|
2327
|
+
});
|
|
2328
|
+
} catch (error) {
|
|
2329
|
+
throw new MastraError(
|
|
2330
|
+
{
|
|
2331
|
+
id: "STORAGE_DYNAMODB_STORE_BATCH_TRACE_INSERT_FAILED",
|
|
2332
|
+
domain: ErrorDomain.STORAGE,
|
|
2333
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2334
|
+
details: { count: records.length }
|
|
2335
|
+
},
|
|
2336
|
+
error
|
|
2337
|
+
);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
async getTracesPaginated(args) {
|
|
2341
|
+
const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
|
|
2342
|
+
this.logger.debug("Getting traces with pagination", { name, scope, page, perPage, attributes, filters, dateRange });
|
|
2343
|
+
try {
|
|
2344
|
+
let query;
|
|
2345
|
+
if (name) {
|
|
2346
|
+
query = this.service.entities.trace.query.byName({ entity: "trace", name });
|
|
2347
|
+
} else if (scope) {
|
|
2348
|
+
query = this.service.entities.trace.query.byScope({ entity: "trace", scope });
|
|
2349
|
+
} else {
|
|
2350
|
+
this.logger.warn("Performing a scan operation on traces - consider using a more specific query");
|
|
2351
|
+
query = this.service.entities.trace.scan;
|
|
2352
|
+
}
|
|
2353
|
+
const results = await query.go({
|
|
2354
|
+
order: "desc",
|
|
2355
|
+
pages: "all"
|
|
2356
|
+
// Get all pages to apply filtering and pagination
|
|
2357
|
+
});
|
|
2358
|
+
if (!results.data.length) {
|
|
2359
|
+
return {
|
|
2360
|
+
traces: [],
|
|
2361
|
+
total: 0,
|
|
2362
|
+
page,
|
|
2363
|
+
perPage,
|
|
2364
|
+
hasMore: false
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
let filteredData = results.data;
|
|
2368
|
+
if (attributes) {
|
|
2369
|
+
filteredData = filteredData.filter((item) => {
|
|
2370
|
+
try {
|
|
2371
|
+
let itemAttributes = {};
|
|
2372
|
+
if (item.attributes) {
|
|
2373
|
+
if (typeof item.attributes === "string") {
|
|
2374
|
+
if (item.attributes === "[object Object]") {
|
|
2375
|
+
itemAttributes = {};
|
|
2376
|
+
} else {
|
|
2377
|
+
try {
|
|
2378
|
+
itemAttributes = JSON.parse(item.attributes);
|
|
2379
|
+
} catch {
|
|
2380
|
+
itemAttributes = {};
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
} else if (typeof item.attributes === "object") {
|
|
2384
|
+
itemAttributes = item.attributes;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
return Object.entries(attributes).every(([key, value]) => itemAttributes[key] === value);
|
|
2388
|
+
} catch (e) {
|
|
2389
|
+
this.logger.warn("Failed to parse attributes during filtering", { item, error: e });
|
|
2390
|
+
return false;
|
|
2391
|
+
}
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
if (dateRange?.start) {
|
|
2395
|
+
filteredData = filteredData.filter((item) => {
|
|
2396
|
+
const itemDate = new Date(item.createdAt);
|
|
2397
|
+
return itemDate >= dateRange.start;
|
|
2398
|
+
});
|
|
2399
|
+
}
|
|
2400
|
+
if (dateRange?.end) {
|
|
2401
|
+
filteredData = filteredData.filter((item) => {
|
|
2402
|
+
const itemDate = new Date(item.createdAt);
|
|
2403
|
+
return itemDate <= dateRange.end;
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
const total = filteredData.length;
|
|
2407
|
+
const start = page * perPage;
|
|
2408
|
+
const end = start + perPage;
|
|
2409
|
+
const paginatedData = filteredData.slice(start, end);
|
|
2410
|
+
const traces = paginatedData.map((item) => {
|
|
2411
|
+
let attributes2;
|
|
2412
|
+
if (item.attributes) {
|
|
2413
|
+
if (typeof item.attributes === "string") {
|
|
2414
|
+
if (item.attributes === "[object Object]") {
|
|
2415
|
+
attributes2 = void 0;
|
|
2416
|
+
} else {
|
|
2417
|
+
try {
|
|
2418
|
+
attributes2 = JSON.parse(item.attributes);
|
|
2419
|
+
} catch {
|
|
2420
|
+
attributes2 = void 0;
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
} else if (typeof item.attributes === "object") {
|
|
2424
|
+
attributes2 = item.attributes;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
let status;
|
|
2428
|
+
if (item.status) {
|
|
2429
|
+
if (typeof item.status === "string") {
|
|
2430
|
+
try {
|
|
2431
|
+
status = JSON.parse(item.status);
|
|
2432
|
+
} catch {
|
|
2433
|
+
status = void 0;
|
|
2434
|
+
}
|
|
2435
|
+
} else if (typeof item.status === "object") {
|
|
2436
|
+
status = item.status;
|
|
2437
|
+
}
|
|
1051
2438
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
2439
|
+
let events;
|
|
2440
|
+
if (item.events) {
|
|
2441
|
+
if (typeof item.events === "string") {
|
|
2442
|
+
try {
|
|
2443
|
+
events = JSON.parse(item.events);
|
|
2444
|
+
} catch {
|
|
2445
|
+
events = void 0;
|
|
2446
|
+
}
|
|
2447
|
+
} else if (Array.isArray(item.events)) {
|
|
2448
|
+
events = item.events;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
let links;
|
|
2452
|
+
if (item.links) {
|
|
2453
|
+
if (typeof item.links === "string") {
|
|
2454
|
+
try {
|
|
2455
|
+
links = JSON.parse(item.links);
|
|
2456
|
+
} catch {
|
|
2457
|
+
links = void 0;
|
|
2458
|
+
}
|
|
2459
|
+
} else if (Array.isArray(item.links)) {
|
|
2460
|
+
links = item.links;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
return {
|
|
2464
|
+
id: item.id,
|
|
2465
|
+
parentSpanId: item.parentSpanId,
|
|
2466
|
+
name: item.name,
|
|
2467
|
+
traceId: item.traceId,
|
|
2468
|
+
scope: item.scope,
|
|
2469
|
+
kind: item.kind,
|
|
2470
|
+
attributes: attributes2,
|
|
2471
|
+
status,
|
|
2472
|
+
events,
|
|
2473
|
+
links,
|
|
2474
|
+
other: item.other,
|
|
2475
|
+
startTime: item.startTime,
|
|
2476
|
+
endTime: item.endTime,
|
|
2477
|
+
createdAt: item.createdAt
|
|
2478
|
+
};
|
|
1070
2479
|
});
|
|
2480
|
+
return {
|
|
2481
|
+
traces,
|
|
2482
|
+
total,
|
|
2483
|
+
page,
|
|
2484
|
+
perPage,
|
|
2485
|
+
hasMore: end < total
|
|
2486
|
+
};
|
|
1071
2487
|
} catch (error) {
|
|
1072
|
-
|
|
1073
|
-
|
|
2488
|
+
throw new MastraError(
|
|
2489
|
+
{
|
|
2490
|
+
id: "STORAGE_DYNAMODB_STORE_GET_TRACES_PAGINATED_FAILED",
|
|
2491
|
+
domain: ErrorDomain.STORAGE,
|
|
2492
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2493
|
+
},
|
|
2494
|
+
error
|
|
2495
|
+
);
|
|
1074
2496
|
}
|
|
1075
2497
|
}
|
|
2498
|
+
};
|
|
2499
|
+
function formatWorkflowRun(snapshotData) {
|
|
2500
|
+
return {
|
|
2501
|
+
workflowName: snapshotData.workflow_name,
|
|
2502
|
+
runId: snapshotData.run_id,
|
|
2503
|
+
snapshot: snapshotData.snapshot,
|
|
2504
|
+
createdAt: new Date(snapshotData.createdAt),
|
|
2505
|
+
updatedAt: new Date(snapshotData.updatedAt),
|
|
2506
|
+
resourceId: snapshotData.resourceId
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
|
|
2510
|
+
service;
|
|
2511
|
+
constructor({ service }) {
|
|
2512
|
+
super();
|
|
2513
|
+
this.service = service;
|
|
2514
|
+
}
|
|
1076
2515
|
// Workflow operations
|
|
1077
2516
|
async persistWorkflowSnapshot({
|
|
1078
2517
|
workflowName,
|
|
@@ -1094,10 +2533,17 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1094
2533
|
updatedAt: now,
|
|
1095
2534
|
resourceId
|
|
1096
2535
|
};
|
|
1097
|
-
await this.service.entities.
|
|
2536
|
+
await this.service.entities.workflow_snapshot.upsert(data).go();
|
|
1098
2537
|
} catch (error) {
|
|
1099
|
-
|
|
1100
|
-
|
|
2538
|
+
throw new MastraError(
|
|
2539
|
+
{
|
|
2540
|
+
id: "STORAGE_DYNAMODB_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
|
|
2541
|
+
domain: ErrorDomain.STORAGE,
|
|
2542
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2543
|
+
details: { workflowName, runId }
|
|
2544
|
+
},
|
|
2545
|
+
error
|
|
2546
|
+
);
|
|
1101
2547
|
}
|
|
1102
2548
|
}
|
|
1103
2549
|
async loadWorkflowSnapshot({
|
|
@@ -1106,7 +2552,7 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1106
2552
|
}) {
|
|
1107
2553
|
this.logger.debug("Loading workflow snapshot", { workflowName, runId });
|
|
1108
2554
|
try {
|
|
1109
|
-
const result = await this.service.entities.
|
|
2555
|
+
const result = await this.service.entities.workflow_snapshot.get({
|
|
1110
2556
|
entity: "workflow_snapshot",
|
|
1111
2557
|
// Add entity type
|
|
1112
2558
|
workflow_name: workflowName,
|
|
@@ -1117,8 +2563,15 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1117
2563
|
}
|
|
1118
2564
|
return result.data.snapshot;
|
|
1119
2565
|
} catch (error) {
|
|
1120
|
-
|
|
1121
|
-
|
|
2566
|
+
throw new MastraError(
|
|
2567
|
+
{
|
|
2568
|
+
id: "STORAGE_DYNAMODB_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
|
|
2569
|
+
domain: ErrorDomain.STORAGE,
|
|
2570
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2571
|
+
details: { workflowName, runId }
|
|
2572
|
+
},
|
|
2573
|
+
error
|
|
2574
|
+
);
|
|
1122
2575
|
}
|
|
1123
2576
|
}
|
|
1124
2577
|
async getWorkflowRuns(args) {
|
|
@@ -1128,14 +2581,14 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1128
2581
|
const offset = args?.offset || 0;
|
|
1129
2582
|
let query;
|
|
1130
2583
|
if (args?.workflowName) {
|
|
1131
|
-
query = this.service.entities.
|
|
2584
|
+
query = this.service.entities.workflow_snapshot.query.primary({
|
|
1132
2585
|
entity: "workflow_snapshot",
|
|
1133
2586
|
// Add entity type
|
|
1134
2587
|
workflow_name: args.workflowName
|
|
1135
2588
|
});
|
|
1136
2589
|
} else {
|
|
1137
2590
|
this.logger.warn("Performing a scan operation on workflow snapshots - consider using a more specific query");
|
|
1138
|
-
query = this.service.entities.
|
|
2591
|
+
query = this.service.entities.workflow_snapshot.scan;
|
|
1139
2592
|
}
|
|
1140
2593
|
const allMatchingSnapshots = [];
|
|
1141
2594
|
let cursor = null;
|
|
@@ -1173,28 +2626,38 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1173
2626
|
}
|
|
1174
2627
|
const total = allMatchingSnapshots.length;
|
|
1175
2628
|
const paginatedData = allMatchingSnapshots.slice(offset, offset + limit);
|
|
1176
|
-
const runs = paginatedData.map((snapshot) =>
|
|
2629
|
+
const runs = paginatedData.map((snapshot) => formatWorkflowRun(snapshot));
|
|
1177
2630
|
return {
|
|
1178
2631
|
runs,
|
|
1179
2632
|
total
|
|
1180
2633
|
};
|
|
1181
2634
|
} catch (error) {
|
|
1182
|
-
|
|
1183
|
-
|
|
2635
|
+
throw new MastraError(
|
|
2636
|
+
{
|
|
2637
|
+
id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUNS_FAILED",
|
|
2638
|
+
domain: ErrorDomain.STORAGE,
|
|
2639
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2640
|
+
details: { workflowName: args?.workflowName || "", resourceId: args?.resourceId || "" }
|
|
2641
|
+
},
|
|
2642
|
+
error
|
|
2643
|
+
);
|
|
1184
2644
|
}
|
|
1185
2645
|
}
|
|
1186
2646
|
async getWorkflowRunById(args) {
|
|
1187
2647
|
const { runId, workflowName } = args;
|
|
1188
2648
|
this.logger.debug("Getting workflow run by ID", { runId, workflowName });
|
|
2649
|
+
console.log("workflowName", workflowName);
|
|
2650
|
+
console.log("runId", runId);
|
|
1189
2651
|
try {
|
|
1190
2652
|
if (workflowName) {
|
|
1191
2653
|
this.logger.debug("WorkflowName provided, using direct GET operation.");
|
|
1192
|
-
const result2 = await this.service.entities.
|
|
2654
|
+
const result2 = await this.service.entities.workflow_snapshot.get({
|
|
1193
2655
|
entity: "workflow_snapshot",
|
|
1194
2656
|
// Entity type for PK
|
|
1195
2657
|
workflow_name: workflowName,
|
|
1196
2658
|
run_id: runId
|
|
1197
2659
|
}).go();
|
|
2660
|
+
console.log("result", result2);
|
|
1198
2661
|
if (!result2.data) {
|
|
1199
2662
|
return null;
|
|
1200
2663
|
}
|
|
@@ -1211,7 +2674,7 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1211
2674
|
this.logger.debug(
|
|
1212
2675
|
'WorkflowName not provided. Attempting to find workflow run by runId using GSI. Ensure GSI (e.g., "byRunId") is defined on the workflowSnapshot entity with run_id as its key and provisioned in DynamoDB.'
|
|
1213
2676
|
);
|
|
1214
|
-
const result = await this.service.entities.
|
|
2677
|
+
const result = await this.service.entities.workflow_snapshot.query.gsi2({ entity: "workflow_snapshot", run_id: runId }).go();
|
|
1215
2678
|
const matchingRunDbItem = result.data && result.data.length > 0 ? result.data[0] : null;
|
|
1216
2679
|
if (!matchingRunDbItem) {
|
|
1217
2680
|
return null;
|
|
@@ -1226,98 +2689,259 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1226
2689
|
resourceId: matchingRunDbItem.resourceId
|
|
1227
2690
|
};
|
|
1228
2691
|
} catch (error) {
|
|
1229
|
-
|
|
1230
|
-
|
|
2692
|
+
throw new MastraError(
|
|
2693
|
+
{
|
|
2694
|
+
id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
|
|
2695
|
+
domain: ErrorDomain.STORAGE,
|
|
2696
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2697
|
+
details: { runId, workflowName: args?.workflowName || "" }
|
|
2698
|
+
},
|
|
2699
|
+
error
|
|
2700
|
+
);
|
|
1231
2701
|
}
|
|
1232
2702
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
2703
|
+
};
|
|
2704
|
+
|
|
2705
|
+
// src/storage/index.ts
|
|
2706
|
+
var DynamoDBStore = class extends MastraStorage {
|
|
2707
|
+
tableName;
|
|
2708
|
+
client;
|
|
2709
|
+
service;
|
|
2710
|
+
hasInitialized = null;
|
|
2711
|
+
stores;
|
|
2712
|
+
constructor({ name, config }) {
|
|
2713
|
+
super({ name });
|
|
2714
|
+
try {
|
|
2715
|
+
if (!config.tableName || typeof config.tableName !== "string" || config.tableName.trim() === "") {
|
|
2716
|
+
throw new Error("DynamoDBStore: config.tableName must be provided and cannot be empty.");
|
|
2717
|
+
}
|
|
2718
|
+
if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
|
|
2719
|
+
throw new Error(
|
|
2720
|
+
`DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2723
|
+
const dynamoClient = new DynamoDBClient({
|
|
2724
|
+
region: config.region || "us-east-1",
|
|
2725
|
+
endpoint: config.endpoint,
|
|
2726
|
+
credentials: config.credentials
|
|
2727
|
+
});
|
|
2728
|
+
this.tableName = config.tableName;
|
|
2729
|
+
this.client = DynamoDBDocumentClient.from(dynamoClient);
|
|
2730
|
+
this.service = getElectroDbService(this.client, this.tableName);
|
|
2731
|
+
const operations = new StoreOperationsDynamoDB({
|
|
2732
|
+
service: this.service,
|
|
2733
|
+
tableName: this.tableName,
|
|
2734
|
+
client: this.client
|
|
2735
|
+
});
|
|
2736
|
+
const traces = new TracesStorageDynamoDB({ service: this.service, operations });
|
|
2737
|
+
const workflows = new WorkflowStorageDynamoDB({ service: this.service });
|
|
2738
|
+
const memory = new MemoryStorageDynamoDB({ service: this.service });
|
|
2739
|
+
const scores = new ScoresStorageDynamoDB({ service: this.service });
|
|
2740
|
+
this.stores = {
|
|
2741
|
+
operations,
|
|
2742
|
+
legacyEvals: new LegacyEvalsDynamoDB({ service: this.service, tableName: this.tableName }),
|
|
2743
|
+
traces,
|
|
2744
|
+
workflows,
|
|
2745
|
+
memory,
|
|
2746
|
+
scores
|
|
2747
|
+
};
|
|
2748
|
+
} catch (error) {
|
|
2749
|
+
throw new MastraError(
|
|
2750
|
+
{
|
|
2751
|
+
id: "STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED",
|
|
2752
|
+
domain: ErrorDomain.STORAGE,
|
|
2753
|
+
category: ErrorCategory.USER
|
|
2754
|
+
},
|
|
2755
|
+
error
|
|
2756
|
+
);
|
|
2757
|
+
}
|
|
1243
2758
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
[TABLE_TRACES]: "trace",
|
|
1252
|
-
[TABLE_RESOURCES]: "resource"
|
|
2759
|
+
get supports() {
|
|
2760
|
+
return {
|
|
2761
|
+
selectByIncludeResourceScope: true,
|
|
2762
|
+
resourceWorkingMemory: true,
|
|
2763
|
+
hasColumn: false,
|
|
2764
|
+
createTable: false,
|
|
2765
|
+
deleteMessages: false
|
|
1253
2766
|
};
|
|
1254
|
-
return mapping[tableName] || null;
|
|
1255
2767
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
2768
|
+
/**
|
|
2769
|
+
* Validates that the required DynamoDB table exists and is accessible.
|
|
2770
|
+
* This does not check the table structure - it assumes the table
|
|
2771
|
+
* was created with the correct structure via CDK/CloudFormation.
|
|
2772
|
+
*/
|
|
2773
|
+
async validateTableExists() {
|
|
1259
2774
|
try {
|
|
1260
|
-
const
|
|
1261
|
-
|
|
1262
|
-
if (!results.data.length) {
|
|
1263
|
-
return [];
|
|
1264
|
-
}
|
|
1265
|
-
let filteredData = results.data;
|
|
1266
|
-
if (type) {
|
|
1267
|
-
filteredData = filteredData.filter((evalRecord) => {
|
|
1268
|
-
try {
|
|
1269
|
-
const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
|
|
1270
|
-
if (type === "test" && !testInfo) {
|
|
1271
|
-
return false;
|
|
1272
|
-
}
|
|
1273
|
-
if (type === "live" && testInfo) {
|
|
1274
|
-
return false;
|
|
1275
|
-
}
|
|
1276
|
-
} catch (e) {
|
|
1277
|
-
this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
|
|
1278
|
-
}
|
|
1279
|
-
return true;
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
return filteredData.map((evalRecord) => {
|
|
1283
|
-
try {
|
|
1284
|
-
return {
|
|
1285
|
-
input: evalRecord.input,
|
|
1286
|
-
output: evalRecord.output,
|
|
1287
|
-
// Safely parse result and test_info
|
|
1288
|
-
result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
|
|
1289
|
-
agentName: evalRecord.agent_name,
|
|
1290
|
-
createdAt: evalRecord.created_at,
|
|
1291
|
-
// Keep as string from DDB?
|
|
1292
|
-
metricName: evalRecord.metric_name,
|
|
1293
|
-
instructions: evalRecord.instructions,
|
|
1294
|
-
runId: evalRecord.run_id,
|
|
1295
|
-
globalRunId: evalRecord.global_run_id,
|
|
1296
|
-
testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
|
|
1297
|
-
};
|
|
1298
|
-
} catch (parseError) {
|
|
1299
|
-
this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
|
|
1300
|
-
return {
|
|
1301
|
-
agentName: evalRecord.agent_name,
|
|
1302
|
-
createdAt: evalRecord.created_at,
|
|
1303
|
-
runId: evalRecord.run_id,
|
|
1304
|
-
globalRunId: evalRecord.global_run_id
|
|
1305
|
-
};
|
|
1306
|
-
}
|
|
2775
|
+
const command = new DescribeTableCommand({
|
|
2776
|
+
TableName: this.tableName
|
|
1307
2777
|
});
|
|
2778
|
+
await this.client.send(command);
|
|
2779
|
+
return true;
|
|
2780
|
+
} catch (error) {
|
|
2781
|
+
if (error.name === "ResourceNotFoundException") {
|
|
2782
|
+
return false;
|
|
2783
|
+
}
|
|
2784
|
+
throw new MastraError(
|
|
2785
|
+
{
|
|
2786
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
|
|
2787
|
+
domain: ErrorDomain.STORAGE,
|
|
2788
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2789
|
+
details: { tableName: this.tableName }
|
|
2790
|
+
},
|
|
2791
|
+
error
|
|
2792
|
+
);
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* Initialize storage, validating the externally managed table is accessible.
|
|
2797
|
+
* For the single-table design, we only validate once that we can access
|
|
2798
|
+
* the table that was created via CDK/CloudFormation.
|
|
2799
|
+
*/
|
|
2800
|
+
async init() {
|
|
2801
|
+
if (this.hasInitialized === null) {
|
|
2802
|
+
this.hasInitialized = this._performInitializationAndStore();
|
|
2803
|
+
}
|
|
2804
|
+
try {
|
|
2805
|
+
await this.hasInitialized;
|
|
1308
2806
|
} catch (error) {
|
|
1309
|
-
|
|
1310
|
-
|
|
2807
|
+
throw new MastraError(
|
|
2808
|
+
{
|
|
2809
|
+
id: "STORAGE_DYNAMODB_STORE_INIT_FAILED",
|
|
2810
|
+
domain: ErrorDomain.STORAGE,
|
|
2811
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2812
|
+
details: { tableName: this.tableName }
|
|
2813
|
+
},
|
|
2814
|
+
error
|
|
2815
|
+
);
|
|
1311
2816
|
}
|
|
1312
2817
|
}
|
|
2818
|
+
/**
|
|
2819
|
+
* Performs the actual table validation and stores the promise.
|
|
2820
|
+
* Handles resetting the stored promise on failure to allow retries.
|
|
2821
|
+
*/
|
|
2822
|
+
_performInitializationAndStore() {
|
|
2823
|
+
return this.validateTableExists().then((exists) => {
|
|
2824
|
+
if (!exists) {
|
|
2825
|
+
throw new Error(
|
|
2826
|
+
`Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
return true;
|
|
2830
|
+
}).catch((err) => {
|
|
2831
|
+
this.hasInitialized = null;
|
|
2832
|
+
throw err;
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
async createTable({ tableName, schema }) {
|
|
2836
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
2837
|
+
}
|
|
2838
|
+
async alterTable(_args) {
|
|
2839
|
+
return this.stores.operations.alterTable(_args);
|
|
2840
|
+
}
|
|
2841
|
+
async clearTable({ tableName }) {
|
|
2842
|
+
return this.stores.operations.clearTable({ tableName });
|
|
2843
|
+
}
|
|
2844
|
+
async dropTable({ tableName }) {
|
|
2845
|
+
return this.stores.operations.dropTable({ tableName });
|
|
2846
|
+
}
|
|
2847
|
+
async insert({ tableName, record }) {
|
|
2848
|
+
return this.stores.operations.insert({ tableName, record });
|
|
2849
|
+
}
|
|
2850
|
+
async batchInsert({ tableName, records }) {
|
|
2851
|
+
return this.stores.operations.batchInsert({ tableName, records });
|
|
2852
|
+
}
|
|
2853
|
+
async load({ tableName, keys }) {
|
|
2854
|
+
return this.stores.operations.load({ tableName, keys });
|
|
2855
|
+
}
|
|
2856
|
+
// Thread operations
|
|
2857
|
+
async getThreadById({ threadId }) {
|
|
2858
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
2859
|
+
}
|
|
2860
|
+
async getThreadsByResourceId({ resourceId }) {
|
|
2861
|
+
return this.stores.memory.getThreadsByResourceId({ resourceId });
|
|
2862
|
+
}
|
|
2863
|
+
async saveThread({ thread }) {
|
|
2864
|
+
return this.stores.memory.saveThread({ thread });
|
|
2865
|
+
}
|
|
2866
|
+
async updateThread({
|
|
2867
|
+
id,
|
|
2868
|
+
title,
|
|
2869
|
+
metadata
|
|
2870
|
+
}) {
|
|
2871
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2872
|
+
}
|
|
2873
|
+
async deleteThread({ threadId }) {
|
|
2874
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
2875
|
+
}
|
|
2876
|
+
async getMessages({
|
|
2877
|
+
threadId,
|
|
2878
|
+
resourceId,
|
|
2879
|
+
selectBy,
|
|
2880
|
+
format
|
|
2881
|
+
}) {
|
|
2882
|
+
return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
|
|
2883
|
+
}
|
|
2884
|
+
async saveMessages(args) {
|
|
2885
|
+
return this.stores.memory.saveMessages(args);
|
|
2886
|
+
}
|
|
2887
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
2888
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
2889
|
+
}
|
|
2890
|
+
async getMessagesPaginated(args) {
|
|
2891
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
2892
|
+
}
|
|
2893
|
+
async updateMessages(_args) {
|
|
2894
|
+
return this.stores.memory.updateMessages(_args);
|
|
2895
|
+
}
|
|
2896
|
+
// Trace operations
|
|
2897
|
+
async getTraces(args) {
|
|
2898
|
+
return this.stores.traces.getTraces(args);
|
|
2899
|
+
}
|
|
2900
|
+
async batchTraceInsert({ records }) {
|
|
2901
|
+
return this.stores.traces.batchTraceInsert({ records });
|
|
2902
|
+
}
|
|
1313
2903
|
async getTracesPaginated(_args) {
|
|
1314
|
-
|
|
2904
|
+
return this.stores.traces.getTracesPaginated(_args);
|
|
2905
|
+
}
|
|
2906
|
+
// Workflow operations
|
|
2907
|
+
async persistWorkflowSnapshot({
|
|
2908
|
+
workflowName,
|
|
2909
|
+
runId,
|
|
2910
|
+
snapshot
|
|
2911
|
+
}) {
|
|
2912
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
2913
|
+
}
|
|
2914
|
+
async loadWorkflowSnapshot({
|
|
2915
|
+
workflowName,
|
|
2916
|
+
runId
|
|
2917
|
+
}) {
|
|
2918
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2919
|
+
}
|
|
2920
|
+
async getWorkflowRuns(args) {
|
|
2921
|
+
return this.stores.workflows.getWorkflowRuns(args);
|
|
2922
|
+
}
|
|
2923
|
+
async getWorkflowRunById(args) {
|
|
2924
|
+
return this.stores.workflows.getWorkflowRunById(args);
|
|
2925
|
+
}
|
|
2926
|
+
async getResourceById({ resourceId }) {
|
|
2927
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
2928
|
+
}
|
|
2929
|
+
async saveResource({ resource }) {
|
|
2930
|
+
return this.stores.memory.saveResource({ resource });
|
|
2931
|
+
}
|
|
2932
|
+
async updateResource({
|
|
2933
|
+
resourceId,
|
|
2934
|
+
workingMemory,
|
|
2935
|
+
metadata
|
|
2936
|
+
}) {
|
|
2937
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
1315
2938
|
}
|
|
1316
|
-
|
|
1317
|
-
|
|
2939
|
+
// Eval operations
|
|
2940
|
+
async getEvalsByAgentName(agentName, type) {
|
|
2941
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
1318
2942
|
}
|
|
1319
|
-
async
|
|
1320
|
-
|
|
2943
|
+
async getEvals(options) {
|
|
2944
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
1321
2945
|
}
|
|
1322
2946
|
/**
|
|
1323
2947
|
* Closes the DynamoDB client connection and cleans up resources.
|
|
@@ -1329,14 +2953,50 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1329
2953
|
this.client.destroy();
|
|
1330
2954
|
this.logger.debug("DynamoDB client closed successfully for store:", { name: this.name });
|
|
1331
2955
|
} catch (error) {
|
|
1332
|
-
|
|
1333
|
-
|
|
2956
|
+
throw new MastraError(
|
|
2957
|
+
{
|
|
2958
|
+
id: "STORAGE_DYNAMODB_STORE_CLOSE_FAILED",
|
|
2959
|
+
domain: ErrorDomain.STORAGE,
|
|
2960
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2961
|
+
},
|
|
2962
|
+
error
|
|
2963
|
+
);
|
|
1334
2964
|
}
|
|
1335
2965
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
2966
|
+
/**
|
|
2967
|
+
* SCORERS - Not implemented
|
|
2968
|
+
*/
|
|
2969
|
+
async getScoreById({ id: _id }) {
|
|
2970
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
2971
|
+
}
|
|
2972
|
+
async saveScore(_score) {
|
|
2973
|
+
return this.stores.scores.saveScore(_score);
|
|
2974
|
+
}
|
|
2975
|
+
async getScoresByRunId({
|
|
2976
|
+
runId: _runId,
|
|
2977
|
+
pagination: _pagination
|
|
2978
|
+
}) {
|
|
2979
|
+
return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
|
|
2980
|
+
}
|
|
2981
|
+
async getScoresByEntityId({
|
|
2982
|
+
entityId: _entityId,
|
|
2983
|
+
entityType: _entityType,
|
|
2984
|
+
pagination: _pagination
|
|
2985
|
+
}) {
|
|
2986
|
+
return this.stores.scores.getScoresByEntityId({
|
|
2987
|
+
entityId: _entityId,
|
|
2988
|
+
entityType: _entityType,
|
|
2989
|
+
pagination: _pagination
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
async getScoresByScorerId({
|
|
2993
|
+
scorerId: _scorerId,
|
|
2994
|
+
pagination: _pagination
|
|
2995
|
+
}) {
|
|
2996
|
+
return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
|
|
1339
2997
|
}
|
|
1340
2998
|
};
|
|
1341
2999
|
|
|
1342
3000
|
export { DynamoDBStore };
|
|
3001
|
+
//# sourceMappingURL=index.js.map
|
|
3002
|
+
//# sourceMappingURL=index.js.map
|