@mastra/dynamodb 0.0.0-share-agent-metadata-with-cloud-20250718123411 → 0.0.0-span-scorring-test-20251124132129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1484 -0
- package/README.md +0 -4
- package/dist/entities/eval.d.ts +102 -0
- package/dist/entities/eval.d.ts.map +1 -0
- package/dist/entities/index.d.ts +761 -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 +244 -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 +1754 -566
- 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 +1755 -567
- package/dist/index.js.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +61 -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 +51 -0
- package/dist/storage/domains/score/index.d.ts.map +1 -0
- package/dist/storage/domains/workflows/index.d.ts +44 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +204 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/package.json +31 -18
- package/dist/_tsup-dts-rollup.d.cts +0 -1160
- package/dist/_tsup-dts-rollup.d.ts +0 -1160
- package/dist/index.d.cts +0 -2
- package/src/entities/eval.ts +0 -102
- package/src/entities/index.ts +0 -23
- package/src/entities/message.ts +0 -143
- package/src/entities/thread.ts +0 -66
- package/src/entities/trace.ts +0 -129
- package/src/entities/utils.ts +0 -51
- package/src/entities/workflow-snapshot.ts +0 -56
- package/src/index.ts +0 -1
- package/src/storage/docker-compose.yml +0 -16
- package/src/storage/index.test.ts +0 -1483
- package/src/storage/index.ts +0 -1383
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
|
|
2
2
|
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
3
|
-
import { MessageList } from '@mastra/core/agent';
|
|
4
3
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
5
|
-
import { MastraStorage, TABLE_TRACES,
|
|
4
|
+
import { MastraStorage, StoreOperations, WorkflowsStorage, normalizePerPage, MemoryStorage, calculatePagination, ScoresStorage, TABLE_SPANS, TABLE_RESOURCES, TABLE_TRACES, TABLE_SCORERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
|
|
6
5
|
import { Entity, Service } from 'electrodb';
|
|
6
|
+
import { MessageList } from '@mastra/core/agent';
|
|
7
|
+
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
7
8
|
|
|
8
9
|
// src/storage/index.ts
|
|
9
10
|
|
|
@@ -294,9 +295,9 @@ var messageEntity = new Entity({
|
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
});
|
|
297
|
-
var
|
|
298
|
+
var resourceEntity = new Entity({
|
|
298
299
|
model: {
|
|
299
|
-
entity: "
|
|
300
|
+
entity: "resource",
|
|
300
301
|
version: "1",
|
|
301
302
|
service: "mastra"
|
|
302
303
|
},
|
|
@@ -310,25 +311,21 @@ var threadEntity = new Entity({
|
|
|
310
311
|
type: "string",
|
|
311
312
|
required: true
|
|
312
313
|
},
|
|
313
|
-
|
|
314
|
-
type: "string",
|
|
315
|
-
required: true
|
|
316
|
-
},
|
|
317
|
-
title: {
|
|
314
|
+
workingMemory: {
|
|
318
315
|
type: "string",
|
|
319
|
-
required:
|
|
316
|
+
required: false
|
|
320
317
|
},
|
|
321
318
|
metadata: {
|
|
322
319
|
type: "string",
|
|
323
320
|
required: false,
|
|
324
|
-
// Stringify
|
|
321
|
+
// Stringify content object on set if it's not already a string
|
|
325
322
|
set: (value) => {
|
|
326
323
|
if (value && typeof value !== "string") {
|
|
327
324
|
return JSON.stringify(value);
|
|
328
325
|
}
|
|
329
326
|
return value;
|
|
330
327
|
},
|
|
331
|
-
// Parse JSON string to object on get
|
|
328
|
+
// Parse JSON string to object on get ONLY if it looks like JSON
|
|
332
329
|
get: (value) => {
|
|
333
330
|
if (value && typeof value === "string") {
|
|
334
331
|
try {
|
|
@@ -346,18 +343,13 @@ var threadEntity = new Entity({
|
|
|
346
343
|
indexes: {
|
|
347
344
|
primary: {
|
|
348
345
|
pk: { field: "pk", composite: ["entity", "id"] },
|
|
349
|
-
sk: { field: "sk", composite: ["
|
|
350
|
-
},
|
|
351
|
-
byResource: {
|
|
352
|
-
index: "gsi1",
|
|
353
|
-
pk: { field: "gsi1pk", composite: ["entity", "resourceId"] },
|
|
354
|
-
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
346
|
+
sk: { field: "sk", composite: ["entity"] }
|
|
355
347
|
}
|
|
356
348
|
}
|
|
357
349
|
});
|
|
358
|
-
var
|
|
350
|
+
var scoreEntity = new Entity({
|
|
359
351
|
model: {
|
|
360
|
-
entity: "
|
|
352
|
+
entity: "score",
|
|
361
353
|
version: "1",
|
|
362
354
|
service: "mastra"
|
|
363
355
|
},
|
|
@@ -371,123 +363,315 @@ var traceEntity = new Entity({
|
|
|
371
363
|
type: "string",
|
|
372
364
|
required: true
|
|
373
365
|
},
|
|
374
|
-
|
|
366
|
+
scorerId: {
|
|
367
|
+
type: "string",
|
|
368
|
+
required: true
|
|
369
|
+
},
|
|
370
|
+
traceId: {
|
|
375
371
|
type: "string",
|
|
376
372
|
required: false
|
|
377
373
|
},
|
|
378
|
-
|
|
374
|
+
spanId: {
|
|
379
375
|
type: "string",
|
|
380
|
-
required:
|
|
376
|
+
required: false
|
|
381
377
|
},
|
|
382
|
-
|
|
378
|
+
runId: {
|
|
383
379
|
type: "string",
|
|
384
380
|
required: true
|
|
385
381
|
},
|
|
386
|
-
|
|
382
|
+
scorer: {
|
|
387
383
|
type: "string",
|
|
388
|
-
required: true
|
|
384
|
+
required: true,
|
|
385
|
+
set: (value) => {
|
|
386
|
+
if (value && typeof value !== "string") {
|
|
387
|
+
return JSON.stringify(value);
|
|
388
|
+
}
|
|
389
|
+
return value;
|
|
390
|
+
},
|
|
391
|
+
get: (value) => {
|
|
392
|
+
if (value && typeof value === "string") {
|
|
393
|
+
try {
|
|
394
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
395
|
+
return JSON.parse(value);
|
|
396
|
+
}
|
|
397
|
+
} catch {
|
|
398
|
+
return value;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return value;
|
|
402
|
+
}
|
|
389
403
|
},
|
|
390
|
-
|
|
404
|
+
extractStepResult: {
|
|
405
|
+
type: "string",
|
|
406
|
+
required: false,
|
|
407
|
+
set: (value) => {
|
|
408
|
+
if (value && typeof value !== "string") {
|
|
409
|
+
return JSON.stringify(value);
|
|
410
|
+
}
|
|
411
|
+
return value;
|
|
412
|
+
},
|
|
413
|
+
get: (value) => {
|
|
414
|
+
if (value && typeof value === "string") {
|
|
415
|
+
try {
|
|
416
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
417
|
+
return JSON.parse(value);
|
|
418
|
+
}
|
|
419
|
+
} catch {
|
|
420
|
+
return value;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return value;
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
preprocessStepResult: {
|
|
427
|
+
type: "string",
|
|
428
|
+
required: false,
|
|
429
|
+
set: (value) => {
|
|
430
|
+
if (value && typeof value !== "string") {
|
|
431
|
+
return JSON.stringify(value);
|
|
432
|
+
}
|
|
433
|
+
return value;
|
|
434
|
+
},
|
|
435
|
+
get: (value) => {
|
|
436
|
+
if (value && typeof value === "string") {
|
|
437
|
+
try {
|
|
438
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
439
|
+
return JSON.parse(value);
|
|
440
|
+
}
|
|
441
|
+
} catch {
|
|
442
|
+
return value;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return value;
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
analyzeStepResult: {
|
|
449
|
+
type: "string",
|
|
450
|
+
required: false,
|
|
451
|
+
set: (value) => {
|
|
452
|
+
if (value && typeof value !== "string") {
|
|
453
|
+
return JSON.stringify(value);
|
|
454
|
+
}
|
|
455
|
+
return value;
|
|
456
|
+
},
|
|
457
|
+
get: (value) => {
|
|
458
|
+
if (value && typeof value === "string") {
|
|
459
|
+
try {
|
|
460
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
461
|
+
return JSON.parse(value);
|
|
462
|
+
}
|
|
463
|
+
} catch {
|
|
464
|
+
return value;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return value;
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
score: {
|
|
391
471
|
type: "number",
|
|
392
472
|
required: true
|
|
393
473
|
},
|
|
394
|
-
|
|
474
|
+
reason: {
|
|
395
475
|
type: "string",
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
476
|
+
required: false
|
|
477
|
+
},
|
|
478
|
+
extractPrompt: {
|
|
479
|
+
type: "string",
|
|
480
|
+
required: false
|
|
481
|
+
},
|
|
482
|
+
analyzePrompt: {
|
|
483
|
+
type: "string",
|
|
484
|
+
required: false
|
|
485
|
+
},
|
|
486
|
+
// Deprecated in favor of generateReasonPrompt
|
|
487
|
+
reasonPrompt: {
|
|
488
|
+
type: "string",
|
|
489
|
+
required: false
|
|
490
|
+
},
|
|
491
|
+
generateScorePrompt: {
|
|
492
|
+
type: "string",
|
|
493
|
+
required: false
|
|
494
|
+
},
|
|
495
|
+
generateReasonPrompt: {
|
|
496
|
+
type: "string",
|
|
497
|
+
required: false
|
|
498
|
+
},
|
|
499
|
+
input: {
|
|
500
|
+
type: "string",
|
|
501
|
+
required: true,
|
|
399
502
|
set: (value) => {
|
|
400
503
|
if (value && typeof value !== "string") {
|
|
401
504
|
return JSON.stringify(value);
|
|
402
505
|
}
|
|
403
506
|
return value;
|
|
404
507
|
},
|
|
405
|
-
// Parse JSON string to object on get
|
|
406
508
|
get: (value) => {
|
|
407
|
-
|
|
509
|
+
if (value && typeof value === "string") {
|
|
510
|
+
try {
|
|
511
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
512
|
+
return JSON.parse(value);
|
|
513
|
+
}
|
|
514
|
+
} catch {
|
|
515
|
+
return value;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return value;
|
|
408
519
|
}
|
|
409
520
|
},
|
|
410
|
-
|
|
521
|
+
output: {
|
|
522
|
+
type: "string",
|
|
523
|
+
required: true,
|
|
524
|
+
set: (value) => {
|
|
525
|
+
if (value && typeof value !== "string") {
|
|
526
|
+
return JSON.stringify(value);
|
|
527
|
+
}
|
|
528
|
+
return value;
|
|
529
|
+
},
|
|
530
|
+
get: (value) => {
|
|
531
|
+
if (value && typeof value === "string") {
|
|
532
|
+
try {
|
|
533
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
534
|
+
return JSON.parse(value);
|
|
535
|
+
}
|
|
536
|
+
} catch {
|
|
537
|
+
return value;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return value;
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
additionalContext: {
|
|
411
544
|
type: "string",
|
|
412
|
-
// JSON stringified
|
|
413
545
|
required: false,
|
|
414
|
-
// Stringify object on set
|
|
415
546
|
set: (value) => {
|
|
416
547
|
if (value && typeof value !== "string") {
|
|
417
548
|
return JSON.stringify(value);
|
|
418
549
|
}
|
|
419
550
|
return value;
|
|
420
551
|
},
|
|
421
|
-
// Parse JSON string to object on get
|
|
422
552
|
get: (value) => {
|
|
553
|
+
if (value && typeof value === "string") {
|
|
554
|
+
try {
|
|
555
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
556
|
+
return JSON.parse(value);
|
|
557
|
+
}
|
|
558
|
+
} catch {
|
|
559
|
+
return value;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
423
562
|
return value;
|
|
424
563
|
}
|
|
425
564
|
},
|
|
426
|
-
|
|
565
|
+
requestContext: {
|
|
427
566
|
type: "string",
|
|
428
|
-
// JSON stringified
|
|
429
567
|
required: false,
|
|
430
|
-
// Stringify object on set
|
|
431
568
|
set: (value) => {
|
|
432
569
|
if (value && typeof value !== "string") {
|
|
433
570
|
return JSON.stringify(value);
|
|
434
571
|
}
|
|
435
572
|
return value;
|
|
436
573
|
},
|
|
437
|
-
// Parse JSON string to object on get
|
|
438
574
|
get: (value) => {
|
|
575
|
+
if (value && typeof value === "string") {
|
|
576
|
+
try {
|
|
577
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
578
|
+
return JSON.parse(value);
|
|
579
|
+
}
|
|
580
|
+
} catch {
|
|
581
|
+
return value;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
439
584
|
return value;
|
|
440
585
|
}
|
|
441
586
|
},
|
|
442
|
-
|
|
587
|
+
entityType: {
|
|
588
|
+
type: "string",
|
|
589
|
+
required: false
|
|
590
|
+
},
|
|
591
|
+
entityData: {
|
|
443
592
|
type: "string",
|
|
444
|
-
// JSON stringified
|
|
445
593
|
required: false,
|
|
446
|
-
// Stringify object on set
|
|
447
594
|
set: (value) => {
|
|
448
595
|
if (value && typeof value !== "string") {
|
|
449
596
|
return JSON.stringify(value);
|
|
450
597
|
}
|
|
451
598
|
return value;
|
|
452
599
|
},
|
|
453
|
-
// Parse JSON string to object on get
|
|
454
600
|
get: (value) => {
|
|
601
|
+
if (value && typeof value === "string") {
|
|
602
|
+
try {
|
|
603
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
604
|
+
return JSON.parse(value);
|
|
605
|
+
}
|
|
606
|
+
} catch {
|
|
607
|
+
return value;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
455
610
|
return value;
|
|
456
611
|
}
|
|
457
612
|
},
|
|
458
|
-
|
|
613
|
+
entityId: {
|
|
459
614
|
type: "string",
|
|
460
615
|
required: false
|
|
461
616
|
},
|
|
462
|
-
|
|
463
|
-
type: "
|
|
617
|
+
source: {
|
|
618
|
+
type: "string",
|
|
464
619
|
required: true
|
|
465
620
|
},
|
|
466
|
-
|
|
467
|
-
type: "
|
|
468
|
-
required:
|
|
621
|
+
resourceId: {
|
|
622
|
+
type: "string",
|
|
623
|
+
required: false
|
|
624
|
+
},
|
|
625
|
+
threadId: {
|
|
626
|
+
type: "string",
|
|
627
|
+
required: false
|
|
469
628
|
}
|
|
470
629
|
},
|
|
471
630
|
indexes: {
|
|
472
631
|
primary: {
|
|
473
632
|
pk: { field: "pk", composite: ["entity", "id"] },
|
|
474
|
-
sk: { field: "sk", composite: [] }
|
|
633
|
+
sk: { field: "sk", composite: ["entity"] }
|
|
475
634
|
},
|
|
476
|
-
|
|
635
|
+
byScorer: {
|
|
477
636
|
index: "gsi1",
|
|
478
|
-
pk: { field: "gsi1pk", composite: ["entity", "
|
|
479
|
-
sk: { field: "gsi1sk", composite: ["
|
|
637
|
+
pk: { field: "gsi1pk", composite: ["entity", "scorerId"] },
|
|
638
|
+
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
480
639
|
},
|
|
481
|
-
|
|
640
|
+
byRun: {
|
|
482
641
|
index: "gsi2",
|
|
483
|
-
pk: { field: "gsi2pk", composite: ["entity", "
|
|
484
|
-
sk: { field: "gsi2sk", composite: ["
|
|
642
|
+
pk: { field: "gsi2pk", composite: ["entity", "runId"] },
|
|
643
|
+
sk: { field: "gsi2sk", composite: ["createdAt"] }
|
|
644
|
+
},
|
|
645
|
+
byTrace: {
|
|
646
|
+
index: "gsi3",
|
|
647
|
+
pk: { field: "gsi3pk", composite: ["entity", "traceId"] },
|
|
648
|
+
sk: { field: "gsi3sk", composite: ["createdAt"] }
|
|
649
|
+
},
|
|
650
|
+
byEntityData: {
|
|
651
|
+
index: "gsi4",
|
|
652
|
+
pk: { field: "gsi4pk", composite: ["entity", "entityId"] },
|
|
653
|
+
sk: { field: "gsi4sk", composite: ["createdAt"] }
|
|
654
|
+
},
|
|
655
|
+
byResource: {
|
|
656
|
+
index: "gsi5",
|
|
657
|
+
pk: { field: "gsi5pk", composite: ["entity", "resourceId"] },
|
|
658
|
+
sk: { field: "gsi5sk", composite: ["createdAt"] }
|
|
659
|
+
},
|
|
660
|
+
byThread: {
|
|
661
|
+
index: "gsi6",
|
|
662
|
+
pk: { field: "gsi6pk", composite: ["entity", "threadId"] },
|
|
663
|
+
sk: { field: "gsi6sk", composite: ["createdAt"] }
|
|
664
|
+
},
|
|
665
|
+
bySpan: {
|
|
666
|
+
index: "gsi7",
|
|
667
|
+
pk: { field: "gsi7pk", composite: ["entity", "traceId", "spanId"] },
|
|
668
|
+
sk: { field: "gsi7sk", composite: ["createdAt"] }
|
|
485
669
|
}
|
|
486
670
|
}
|
|
487
671
|
});
|
|
488
|
-
var
|
|
672
|
+
var threadEntity = new Entity({
|
|
489
673
|
model: {
|
|
490
|
-
entity: "
|
|
674
|
+
entity: "thread",
|
|
491
675
|
version: "1",
|
|
492
676
|
service: "mastra"
|
|
493
677
|
},
|
|
@@ -497,11 +681,202 @@ var workflowSnapshotEntity = new Entity({
|
|
|
497
681
|
required: true
|
|
498
682
|
},
|
|
499
683
|
...baseAttributes,
|
|
500
|
-
|
|
684
|
+
id: {
|
|
501
685
|
type: "string",
|
|
502
686
|
required: true
|
|
503
687
|
},
|
|
504
|
-
|
|
688
|
+
resourceId: {
|
|
689
|
+
type: "string",
|
|
690
|
+
required: true
|
|
691
|
+
},
|
|
692
|
+
title: {
|
|
693
|
+
type: "string",
|
|
694
|
+
required: true
|
|
695
|
+
},
|
|
696
|
+
metadata: {
|
|
697
|
+
type: "string",
|
|
698
|
+
required: false,
|
|
699
|
+
// Stringify metadata object on set if it's not already a string
|
|
700
|
+
set: (value) => {
|
|
701
|
+
if (value && typeof value !== "string") {
|
|
702
|
+
return JSON.stringify(value);
|
|
703
|
+
}
|
|
704
|
+
return value;
|
|
705
|
+
},
|
|
706
|
+
// Parse JSON string to object on get
|
|
707
|
+
get: (value) => {
|
|
708
|
+
if (value && typeof value === "string") {
|
|
709
|
+
try {
|
|
710
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
711
|
+
return JSON.parse(value);
|
|
712
|
+
}
|
|
713
|
+
} catch {
|
|
714
|
+
return value;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return value;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
},
|
|
721
|
+
indexes: {
|
|
722
|
+
primary: {
|
|
723
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
724
|
+
sk: { field: "sk", composite: ["id"] }
|
|
725
|
+
},
|
|
726
|
+
byResource: {
|
|
727
|
+
index: "gsi1",
|
|
728
|
+
pk: { field: "gsi1pk", composite: ["entity", "resourceId"] },
|
|
729
|
+
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
var traceEntity = new Entity({
|
|
734
|
+
model: {
|
|
735
|
+
entity: "trace",
|
|
736
|
+
version: "1",
|
|
737
|
+
service: "mastra"
|
|
738
|
+
},
|
|
739
|
+
attributes: {
|
|
740
|
+
entity: {
|
|
741
|
+
type: "string",
|
|
742
|
+
required: true
|
|
743
|
+
},
|
|
744
|
+
...baseAttributes,
|
|
745
|
+
id: {
|
|
746
|
+
type: "string",
|
|
747
|
+
required: true
|
|
748
|
+
},
|
|
749
|
+
parentSpanId: {
|
|
750
|
+
type: "string",
|
|
751
|
+
required: false
|
|
752
|
+
},
|
|
753
|
+
name: {
|
|
754
|
+
type: "string",
|
|
755
|
+
required: true
|
|
756
|
+
},
|
|
757
|
+
traceId: {
|
|
758
|
+
type: "string",
|
|
759
|
+
required: true
|
|
760
|
+
},
|
|
761
|
+
scope: {
|
|
762
|
+
type: "string",
|
|
763
|
+
required: true
|
|
764
|
+
},
|
|
765
|
+
kind: {
|
|
766
|
+
type: "number",
|
|
767
|
+
required: true
|
|
768
|
+
},
|
|
769
|
+
attributes: {
|
|
770
|
+
type: "string",
|
|
771
|
+
// JSON stringified
|
|
772
|
+
required: false,
|
|
773
|
+
// Stringify object on set
|
|
774
|
+
set: (value) => {
|
|
775
|
+
if (value && typeof value !== "string") {
|
|
776
|
+
return JSON.stringify(value);
|
|
777
|
+
}
|
|
778
|
+
return value;
|
|
779
|
+
},
|
|
780
|
+
// Parse JSON string to object on get
|
|
781
|
+
get: (value) => {
|
|
782
|
+
return value ? JSON.parse(value) : value;
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
status: {
|
|
786
|
+
type: "string",
|
|
787
|
+
// JSON stringified
|
|
788
|
+
required: false,
|
|
789
|
+
// Stringify object on set
|
|
790
|
+
set: (value) => {
|
|
791
|
+
if (value && typeof value !== "string") {
|
|
792
|
+
return JSON.stringify(value);
|
|
793
|
+
}
|
|
794
|
+
return value;
|
|
795
|
+
},
|
|
796
|
+
// Parse JSON string to object on get
|
|
797
|
+
get: (value) => {
|
|
798
|
+
return value;
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
events: {
|
|
802
|
+
type: "string",
|
|
803
|
+
// JSON stringified
|
|
804
|
+
required: false,
|
|
805
|
+
// Stringify object on set
|
|
806
|
+
set: (value) => {
|
|
807
|
+
if (value && typeof value !== "string") {
|
|
808
|
+
return JSON.stringify(value);
|
|
809
|
+
}
|
|
810
|
+
return value;
|
|
811
|
+
},
|
|
812
|
+
// Parse JSON string to object on get
|
|
813
|
+
get: (value) => {
|
|
814
|
+
return value;
|
|
815
|
+
}
|
|
816
|
+
},
|
|
817
|
+
links: {
|
|
818
|
+
type: "string",
|
|
819
|
+
// JSON stringified
|
|
820
|
+
required: false,
|
|
821
|
+
// Stringify object on set
|
|
822
|
+
set: (value) => {
|
|
823
|
+
if (value && typeof value !== "string") {
|
|
824
|
+
return JSON.stringify(value);
|
|
825
|
+
}
|
|
826
|
+
return value;
|
|
827
|
+
},
|
|
828
|
+
// Parse JSON string to object on get
|
|
829
|
+
get: (value) => {
|
|
830
|
+
return value;
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
other: {
|
|
834
|
+
type: "string",
|
|
835
|
+
required: false
|
|
836
|
+
},
|
|
837
|
+
startTime: {
|
|
838
|
+
type: "number",
|
|
839
|
+
required: true
|
|
840
|
+
},
|
|
841
|
+
endTime: {
|
|
842
|
+
type: "number",
|
|
843
|
+
required: true
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
indexes: {
|
|
847
|
+
primary: {
|
|
848
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
849
|
+
sk: { field: "sk", composite: [] }
|
|
850
|
+
},
|
|
851
|
+
byName: {
|
|
852
|
+
index: "gsi1",
|
|
853
|
+
pk: { field: "gsi1pk", composite: ["entity", "name"] },
|
|
854
|
+
sk: { field: "gsi1sk", composite: ["startTime"] }
|
|
855
|
+
},
|
|
856
|
+
byScope: {
|
|
857
|
+
index: "gsi2",
|
|
858
|
+
pk: { field: "gsi2pk", composite: ["entity", "scope"] },
|
|
859
|
+
sk: { field: "gsi2sk", composite: ["startTime"] }
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
var workflowSnapshotEntity = new Entity({
|
|
864
|
+
model: {
|
|
865
|
+
entity: "workflow_snapshot",
|
|
866
|
+
version: "1",
|
|
867
|
+
service: "mastra"
|
|
868
|
+
},
|
|
869
|
+
attributes: {
|
|
870
|
+
entity: {
|
|
871
|
+
type: "string",
|
|
872
|
+
required: true
|
|
873
|
+
},
|
|
874
|
+
...baseAttributes,
|
|
875
|
+
workflow_name: {
|
|
876
|
+
type: "string",
|
|
877
|
+
required: true
|
|
878
|
+
},
|
|
879
|
+
run_id: {
|
|
505
880
|
type: "string",
|
|
506
881
|
required: true
|
|
507
882
|
},
|
|
@@ -548,7 +923,9 @@ function getElectroDbService(client, tableName) {
|
|
|
548
923
|
message: messageEntity,
|
|
549
924
|
eval: evalEntity,
|
|
550
925
|
trace: traceEntity,
|
|
551
|
-
|
|
926
|
+
workflow_snapshot: workflowSnapshotEntity,
|
|
927
|
+
resource: resourceEntity,
|
|
928
|
+
score: scoreEntity
|
|
552
929
|
},
|
|
553
930
|
{
|
|
554
931
|
client,
|
|
@@ -556,76 +933,771 @@ function getElectroDbService(client, tableName) {
|
|
|
556
933
|
}
|
|
557
934
|
);
|
|
558
935
|
}
|
|
559
|
-
|
|
560
|
-
// src/storage/index.ts
|
|
561
|
-
var DynamoDBStore = class extends MastraStorage {
|
|
562
|
-
tableName;
|
|
563
|
-
client;
|
|
936
|
+
var MemoryStorageDynamoDB = class extends MemoryStorage {
|
|
564
937
|
service;
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
938
|
+
constructor({ service }) {
|
|
939
|
+
super();
|
|
940
|
+
this.service = service;
|
|
941
|
+
}
|
|
942
|
+
// Helper function to parse message data (handle JSON fields)
|
|
943
|
+
parseMessageData(data) {
|
|
944
|
+
return {
|
|
945
|
+
...data,
|
|
946
|
+
// Ensure dates are Date objects if needed (ElectroDB might return strings)
|
|
947
|
+
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
948
|
+
updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0
|
|
949
|
+
// Other fields like content, toolCallArgs etc. are assumed to be correctly
|
|
950
|
+
// transformed by the ElectroDB entity getters.
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
// Helper function to transform and sort threads
|
|
954
|
+
transformAndSortThreads(rawThreads, field, direction) {
|
|
955
|
+
return rawThreads.map((data) => ({
|
|
956
|
+
...data,
|
|
957
|
+
// Convert date strings back to Date objects for consistency
|
|
958
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
959
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
960
|
+
})).sort((a, b) => {
|
|
961
|
+
const fieldA = field === "createdAt" ? a.createdAt : a.updatedAt;
|
|
962
|
+
const fieldB = field === "createdAt" ? b.createdAt : b.updatedAt;
|
|
963
|
+
const comparison = fieldA.getTime() - fieldB.getTime();
|
|
964
|
+
return direction === "DESC" ? -comparison : comparison;
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
async getThreadById({ threadId }) {
|
|
968
|
+
this.logger.debug("Getting thread by ID", { threadId });
|
|
568
969
|
try {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
|
|
573
|
-
throw new Error(
|
|
574
|
-
`DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
|
|
575
|
-
);
|
|
970
|
+
const result = await this.service.entities.thread.get({ entity: "thread", id: threadId }).go();
|
|
971
|
+
if (!result.data) {
|
|
972
|
+
return null;
|
|
576
973
|
}
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
974
|
+
const data = result.data;
|
|
975
|
+
return {
|
|
976
|
+
...data,
|
|
977
|
+
// Convert date strings back to Date objects for consistency
|
|
978
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
979
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
980
|
+
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
981
|
+
// metadata is already transformed by the entity's getter
|
|
982
|
+
};
|
|
585
983
|
} catch (error) {
|
|
586
984
|
throw new MastraError(
|
|
587
985
|
{
|
|
588
|
-
id: "
|
|
986
|
+
id: "STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED",
|
|
589
987
|
domain: ErrorDomain.STORAGE,
|
|
590
|
-
category: ErrorCategory.
|
|
988
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
989
|
+
details: { threadId }
|
|
591
990
|
},
|
|
592
991
|
error
|
|
593
992
|
);
|
|
594
993
|
}
|
|
595
994
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
995
|
+
async saveThread({ thread }) {
|
|
996
|
+
this.logger.debug("Saving thread", { threadId: thread.id });
|
|
997
|
+
const now = /* @__PURE__ */ new Date();
|
|
998
|
+
const threadData = {
|
|
999
|
+
entity: "thread",
|
|
1000
|
+
id: thread.id,
|
|
1001
|
+
resourceId: thread.resourceId,
|
|
1002
|
+
title: thread.title || `Thread ${thread.id}`,
|
|
1003
|
+
createdAt: thread.createdAt?.toISOString() || now.toISOString(),
|
|
1004
|
+
updatedAt: thread.updatedAt?.toISOString() || now.toISOString(),
|
|
1005
|
+
metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
|
|
1006
|
+
};
|
|
605
1007
|
try {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
1008
|
+
await this.service.entities.thread.upsert(threadData).go();
|
|
1009
|
+
return {
|
|
1010
|
+
id: thread.id,
|
|
1011
|
+
resourceId: thread.resourceId,
|
|
1012
|
+
title: threadData.title,
|
|
1013
|
+
createdAt: thread.createdAt || now,
|
|
1014
|
+
updatedAt: thread.updatedAt || now,
|
|
1015
|
+
metadata: thread.metadata
|
|
1016
|
+
};
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
throw new MastraError(
|
|
1019
|
+
{
|
|
1020
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED",
|
|
1021
|
+
domain: ErrorDomain.STORAGE,
|
|
1022
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1023
|
+
details: { threadId: thread.id }
|
|
1024
|
+
},
|
|
1025
|
+
error
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
async updateThread({
|
|
1030
|
+
id,
|
|
1031
|
+
title,
|
|
1032
|
+
metadata
|
|
1033
|
+
}) {
|
|
1034
|
+
this.logger.debug("Updating thread", { threadId: id });
|
|
1035
|
+
try {
|
|
1036
|
+
const existingThread = await this.getThreadById({ threadId: id });
|
|
1037
|
+
if (!existingThread) {
|
|
1038
|
+
throw new Error(`Thread not found: ${id}`);
|
|
1039
|
+
}
|
|
1040
|
+
const now = /* @__PURE__ */ new Date();
|
|
1041
|
+
const updateData = {
|
|
1042
|
+
updatedAt: now.toISOString()
|
|
1043
|
+
};
|
|
1044
|
+
if (title) {
|
|
1045
|
+
updateData.title = title;
|
|
1046
|
+
}
|
|
1047
|
+
if (metadata) {
|
|
1048
|
+
const existingMetadata = existingThread.metadata ? typeof existingThread.metadata === "string" ? JSON.parse(existingThread.metadata) : existingThread.metadata : {};
|
|
1049
|
+
const mergedMetadata = { ...existingMetadata, ...metadata };
|
|
1050
|
+
updateData.metadata = JSON.stringify(mergedMetadata);
|
|
1051
|
+
}
|
|
1052
|
+
await this.service.entities.thread.update({ entity: "thread", id }).set(updateData).go();
|
|
1053
|
+
return {
|
|
1054
|
+
...existingThread,
|
|
1055
|
+
title: title || existingThread.title,
|
|
1056
|
+
metadata: metadata ? { ...existingThread.metadata, ...metadata } : existingThread.metadata,
|
|
1057
|
+
updatedAt: now
|
|
1058
|
+
};
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
throw new MastraError(
|
|
1061
|
+
{
|
|
1062
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED",
|
|
1063
|
+
domain: ErrorDomain.STORAGE,
|
|
1064
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1065
|
+
details: { threadId: id }
|
|
1066
|
+
},
|
|
1067
|
+
error
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
async deleteThread({ threadId }) {
|
|
1072
|
+
this.logger.debug("Deleting thread", { threadId });
|
|
1073
|
+
try {
|
|
1074
|
+
const { messages } = await this.listMessages({ threadId, perPage: false });
|
|
1075
|
+
if (messages.length > 0) {
|
|
1076
|
+
const batchSize = 25;
|
|
1077
|
+
for (let i = 0; i < messages.length; i += batchSize) {
|
|
1078
|
+
const batch = messages.slice(i, i + batchSize);
|
|
1079
|
+
await Promise.all(
|
|
1080
|
+
batch.map(
|
|
1081
|
+
(message) => this.service.entities.message.delete({
|
|
1082
|
+
entity: "message",
|
|
1083
|
+
id: message.id,
|
|
1084
|
+
threadId: message.threadId
|
|
1085
|
+
}).go()
|
|
1086
|
+
)
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
await this.service.entities.thread.delete({ entity: "thread", id: threadId }).go();
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
throw new MastraError(
|
|
1093
|
+
{
|
|
1094
|
+
id: "STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED",
|
|
1095
|
+
domain: ErrorDomain.STORAGE,
|
|
1096
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1097
|
+
details: { threadId }
|
|
1098
|
+
},
|
|
1099
|
+
error
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
async listMessagesById({ messageIds }) {
|
|
1104
|
+
this.logger.debug("Getting messages by ID", { messageIds });
|
|
1105
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
1106
|
+
try {
|
|
1107
|
+
const results = await Promise.all(
|
|
1108
|
+
messageIds.map((id) => this.service.entities.message.query.primary({ entity: "message", id }).go())
|
|
1109
|
+
);
|
|
1110
|
+
const data = results.map((result) => result.data).flat(1);
|
|
1111
|
+
let parsedMessages = data.map((data2) => this.parseMessageData(data2)).filter((msg) => "content" in msg);
|
|
1112
|
+
const uniqueMessages = parsedMessages.filter(
|
|
1113
|
+
(message, index, self) => index === self.findIndex((m) => m.id === message.id)
|
|
1114
|
+
);
|
|
1115
|
+
const list = new MessageList().add(uniqueMessages, "memory");
|
|
1116
|
+
return { messages: list.get.all.db() };
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
throw new MastraError(
|
|
1119
|
+
{
|
|
1120
|
+
id: "STORAGE_DYNAMODB_STORE_LIST_MESSAGES_BY_ID_FAILED",
|
|
1121
|
+
domain: ErrorDomain.STORAGE,
|
|
1122
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1123
|
+
details: { messageIds: JSON.stringify(messageIds) }
|
|
1124
|
+
},
|
|
1125
|
+
error
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
async listMessages(args) {
|
|
1130
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
1131
|
+
if (!threadId.trim()) {
|
|
1132
|
+
throw new MastraError(
|
|
1133
|
+
{
|
|
1134
|
+
id: "STORAGE_DYNAMODB_LIST_MESSAGES_INVALID_THREAD_ID",
|
|
1135
|
+
domain: ErrorDomain.STORAGE,
|
|
1136
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1137
|
+
details: { threadId }
|
|
1138
|
+
},
|
|
1139
|
+
new Error("threadId must be a non-empty string")
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
const perPage = normalizePerPage(perPageInput, 40);
|
|
1143
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1144
|
+
try {
|
|
1145
|
+
if (page < 0) {
|
|
1146
|
+
throw new MastraError(
|
|
1147
|
+
{
|
|
1148
|
+
id: "STORAGE_DYNAMODB_LIST_MESSAGES_INVALID_PAGE",
|
|
1149
|
+
domain: ErrorDomain.STORAGE,
|
|
1150
|
+
category: ErrorCategory.USER,
|
|
1151
|
+
details: { page }
|
|
1152
|
+
},
|
|
1153
|
+
new Error("page must be >= 0")
|
|
613
1154
|
);
|
|
614
1155
|
}
|
|
615
|
-
|
|
1156
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
1157
|
+
this.logger.debug("Getting messages with listMessages", {
|
|
1158
|
+
threadId,
|
|
1159
|
+
resourceId,
|
|
1160
|
+
perPageInput,
|
|
1161
|
+
offset,
|
|
1162
|
+
perPage,
|
|
1163
|
+
page,
|
|
1164
|
+
field,
|
|
1165
|
+
direction
|
|
1166
|
+
});
|
|
1167
|
+
const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
|
|
1168
|
+
const results = await query.go();
|
|
1169
|
+
let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg && typeof msg.content === "object");
|
|
1170
|
+
if (resourceId) {
|
|
1171
|
+
allThreadMessages = allThreadMessages.filter((msg) => msg.resourceId === resourceId);
|
|
1172
|
+
}
|
|
1173
|
+
if (filter?.dateRange) {
|
|
1174
|
+
const dateRange = filter.dateRange;
|
|
1175
|
+
allThreadMessages = allThreadMessages.filter((msg) => {
|
|
1176
|
+
const createdAt = new Date(msg.createdAt).getTime();
|
|
1177
|
+
if (dateRange.start) {
|
|
1178
|
+
const startTime = dateRange.start instanceof Date ? dateRange.start.getTime() : new Date(dateRange.start).getTime();
|
|
1179
|
+
if (createdAt < startTime) return false;
|
|
1180
|
+
}
|
|
1181
|
+
if (dateRange.end) {
|
|
1182
|
+
const endTime = dateRange.end instanceof Date ? dateRange.end.getTime() : new Date(dateRange.end).getTime();
|
|
1183
|
+
if (createdAt > endTime) return false;
|
|
1184
|
+
}
|
|
1185
|
+
return true;
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
allThreadMessages.sort((a, b) => {
|
|
1189
|
+
const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
1190
|
+
const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
1191
|
+
if (aValue === bValue) {
|
|
1192
|
+
return a.id.localeCompare(b.id);
|
|
1193
|
+
}
|
|
1194
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
1195
|
+
});
|
|
1196
|
+
const total = allThreadMessages.length;
|
|
1197
|
+
const paginatedMessages = allThreadMessages.slice(offset, offset + perPage);
|
|
1198
|
+
const paginatedCount = paginatedMessages.length;
|
|
1199
|
+
if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) {
|
|
1200
|
+
return {
|
|
1201
|
+
messages: [],
|
|
1202
|
+
total: 0,
|
|
1203
|
+
page,
|
|
1204
|
+
perPage: perPageForResponse,
|
|
1205
|
+
hasMore: false
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
const messageIds = new Set(paginatedMessages.map((m) => m.id));
|
|
1209
|
+
let includeMessages = [];
|
|
1210
|
+
if (include && include.length > 0) {
|
|
1211
|
+
const selectBy = { include };
|
|
1212
|
+
includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1213
|
+
for (const includeMsg of includeMessages) {
|
|
1214
|
+
if (!messageIds.has(includeMsg.id)) {
|
|
1215
|
+
paginatedMessages.push(includeMsg);
|
|
1216
|
+
messageIds.add(includeMsg.id);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
const list = new MessageList().add(paginatedMessages, "memory");
|
|
1221
|
+
let finalMessages = list.get.all.db();
|
|
1222
|
+
finalMessages = finalMessages.sort((a, b) => {
|
|
1223
|
+
const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
1224
|
+
const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
1225
|
+
if (aValue === bValue) {
|
|
1226
|
+
return a.id.localeCompare(b.id);
|
|
1227
|
+
}
|
|
1228
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
1229
|
+
});
|
|
1230
|
+
const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
|
|
1231
|
+
const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
|
|
1232
|
+
let hasMore = false;
|
|
1233
|
+
if (perPageInput !== false && !allThreadMessagesReturned) {
|
|
1234
|
+
hasMore = offset + paginatedCount < total;
|
|
1235
|
+
}
|
|
1236
|
+
return {
|
|
1237
|
+
messages: finalMessages,
|
|
1238
|
+
total,
|
|
1239
|
+
page,
|
|
1240
|
+
perPage: perPageForResponse,
|
|
1241
|
+
hasMore
|
|
1242
|
+
};
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
const mastraError = new MastraError(
|
|
1245
|
+
{
|
|
1246
|
+
id: "STORAGE_DYNAMODB_STORE_LIST_MESSAGES_FAILED",
|
|
1247
|
+
domain: ErrorDomain.STORAGE,
|
|
1248
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1249
|
+
details: {
|
|
1250
|
+
threadId,
|
|
1251
|
+
resourceId: resourceId ?? ""
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
error
|
|
1255
|
+
);
|
|
1256
|
+
this.logger?.error?.(mastraError.toString());
|
|
1257
|
+
this.logger?.trackException?.(mastraError);
|
|
1258
|
+
return {
|
|
1259
|
+
messages: [],
|
|
1260
|
+
total: 0,
|
|
1261
|
+
page,
|
|
1262
|
+
perPage: perPageForResponse,
|
|
1263
|
+
hasMore: false
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
async saveMessages(args) {
|
|
1268
|
+
const { messages } = args;
|
|
1269
|
+
this.logger.debug("Saving messages", { count: messages.length });
|
|
1270
|
+
if (!messages.length) {
|
|
1271
|
+
return { messages: [] };
|
|
1272
|
+
}
|
|
1273
|
+
const threadId = messages[0]?.threadId;
|
|
1274
|
+
if (!threadId) {
|
|
1275
|
+
throw new Error("Thread ID is required");
|
|
1276
|
+
}
|
|
1277
|
+
const messagesToSave = messages.map((msg) => {
|
|
1278
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1279
|
+
return {
|
|
1280
|
+
entity: "message",
|
|
1281
|
+
// Add entity type
|
|
1282
|
+
id: msg.id,
|
|
1283
|
+
threadId: msg.threadId,
|
|
1284
|
+
role: msg.role,
|
|
1285
|
+
type: msg.type,
|
|
1286
|
+
resourceId: msg.resourceId,
|
|
1287
|
+
// Ensure complex fields are stringified if not handled by attribute setters
|
|
1288
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
1289
|
+
toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
|
|
1290
|
+
toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
|
|
1291
|
+
toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
|
|
1292
|
+
createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
|
|
1293
|
+
updatedAt: now
|
|
1294
|
+
// Add updatedAt
|
|
1295
|
+
};
|
|
1296
|
+
});
|
|
1297
|
+
try {
|
|
1298
|
+
const savedMessageIds = [];
|
|
1299
|
+
for (const messageData of messagesToSave) {
|
|
1300
|
+
if (!messageData.entity) {
|
|
1301
|
+
this.logger.error("Missing entity property in message data for create", { messageData });
|
|
1302
|
+
throw new Error("Internal error: Missing entity property during saveMessages");
|
|
1303
|
+
}
|
|
1304
|
+
try {
|
|
1305
|
+
await this.service.entities.message.put(messageData).go();
|
|
1306
|
+
savedMessageIds.push(messageData.id);
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
for (const savedId of savedMessageIds) {
|
|
1309
|
+
try {
|
|
1310
|
+
await this.service.entities.message.delete({ entity: "message", id: savedId }).go();
|
|
1311
|
+
} catch (rollbackError) {
|
|
1312
|
+
this.logger.error("Failed to rollback message during save error", {
|
|
1313
|
+
messageId: savedId,
|
|
1314
|
+
error: rollbackError
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
throw error;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
|
|
1322
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1323
|
+
}).go();
|
|
1324
|
+
const list = new MessageList().add(messages, "memory");
|
|
1325
|
+
return { messages: list.get.all.db() };
|
|
616
1326
|
} catch (error) {
|
|
617
|
-
this.logger.error("Error validating table access", { tableName: this.tableName, error });
|
|
618
1327
|
throw new MastraError(
|
|
619
1328
|
{
|
|
620
|
-
id: "
|
|
1329
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED",
|
|
621
1330
|
domain: ErrorDomain.STORAGE,
|
|
622
1331
|
category: ErrorCategory.THIRD_PARTY,
|
|
623
|
-
details: {
|
|
1332
|
+
details: { count: messages.length }
|
|
1333
|
+
},
|
|
1334
|
+
error
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
async listThreadsByResourceId(args) {
|
|
1339
|
+
const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
|
|
1340
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1341
|
+
if (page < 0) {
|
|
1342
|
+
throw new MastraError(
|
|
1343
|
+
{
|
|
1344
|
+
id: "STORAGE_DYNAMODB_LIST_THREADS_BY_RESOURCE_ID_INVALID_PAGE",
|
|
1345
|
+
domain: ErrorDomain.STORAGE,
|
|
1346
|
+
category: ErrorCategory.USER,
|
|
1347
|
+
details: { page }
|
|
1348
|
+
},
|
|
1349
|
+
new Error("page must be >= 0")
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1353
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
1354
|
+
this.logger.debug("Getting threads by resource ID with pagination", {
|
|
1355
|
+
resourceId,
|
|
1356
|
+
page,
|
|
1357
|
+
perPage,
|
|
1358
|
+
field,
|
|
1359
|
+
direction
|
|
1360
|
+
});
|
|
1361
|
+
try {
|
|
1362
|
+
const query = this.service.entities.thread.query.byResource({ entity: "thread", resourceId });
|
|
1363
|
+
const results = await query.go();
|
|
1364
|
+
const allThreads = this.transformAndSortThreads(results.data, field, direction);
|
|
1365
|
+
const endIndex = offset + perPage;
|
|
1366
|
+
const paginatedThreads = allThreads.slice(offset, endIndex);
|
|
1367
|
+
const total = allThreads.length;
|
|
1368
|
+
const hasMore = offset + perPage < total;
|
|
1369
|
+
return {
|
|
1370
|
+
threads: paginatedThreads,
|
|
1371
|
+
total,
|
|
1372
|
+
page,
|
|
1373
|
+
perPage: perPageForResponse,
|
|
1374
|
+
hasMore
|
|
1375
|
+
};
|
|
1376
|
+
} catch (error) {
|
|
1377
|
+
throw new MastraError(
|
|
1378
|
+
{
|
|
1379
|
+
id: "DYNAMODB_STORAGE_LIST_THREADS_BY_RESOURCE_ID_FAILED",
|
|
1380
|
+
domain: ErrorDomain.STORAGE,
|
|
1381
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1382
|
+
details: { resourceId, page, perPage }
|
|
1383
|
+
},
|
|
1384
|
+
error
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
// Helper method to get included messages with context
|
|
1389
|
+
async _getIncludedMessages(threadId, selectBy) {
|
|
1390
|
+
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
1391
|
+
if (!selectBy?.include?.length) {
|
|
1392
|
+
return [];
|
|
1393
|
+
}
|
|
1394
|
+
const includeMessages = [];
|
|
1395
|
+
for (const includeItem of selectBy.include) {
|
|
1396
|
+
try {
|
|
1397
|
+
const { id, threadId: targetThreadId, withPreviousMessages = 0, withNextMessages = 0 } = includeItem;
|
|
1398
|
+
const searchThreadId = targetThreadId || threadId;
|
|
1399
|
+
this.logger.debug("Getting included messages for", {
|
|
1400
|
+
id,
|
|
1401
|
+
targetThreadId,
|
|
1402
|
+
searchThreadId,
|
|
1403
|
+
withPreviousMessages,
|
|
1404
|
+
withNextMessages
|
|
1405
|
+
});
|
|
1406
|
+
const query = this.service.entities.message.query.byThread({ entity: "message", threadId: searchThreadId });
|
|
1407
|
+
const results = await query.go();
|
|
1408
|
+
const allMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg && typeof msg.content === "object");
|
|
1409
|
+
this.logger.debug("Found messages in thread", {
|
|
1410
|
+
threadId: searchThreadId,
|
|
1411
|
+
messageCount: allMessages.length,
|
|
1412
|
+
messageIds: allMessages.map((m) => m.id)
|
|
1413
|
+
});
|
|
1414
|
+
allMessages.sort((a, b) => {
|
|
1415
|
+
const timeA = a.createdAt.getTime();
|
|
1416
|
+
const timeB = b.createdAt.getTime();
|
|
1417
|
+
if (timeA === timeB) {
|
|
1418
|
+
return a.id.localeCompare(b.id);
|
|
1419
|
+
}
|
|
1420
|
+
return timeA - timeB;
|
|
1421
|
+
});
|
|
1422
|
+
const targetIndex = allMessages.findIndex((msg) => msg.id === id);
|
|
1423
|
+
if (targetIndex === -1) {
|
|
1424
|
+
this.logger.warn("Target message not found", { id, threadId: searchThreadId });
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
this.logger.debug("Found target message at index", { id, targetIndex, totalMessages: allMessages.length });
|
|
1428
|
+
const startIndex = Math.max(0, targetIndex - withPreviousMessages);
|
|
1429
|
+
const endIndex = Math.min(allMessages.length, targetIndex + withNextMessages + 1);
|
|
1430
|
+
const contextMessages = allMessages.slice(startIndex, endIndex);
|
|
1431
|
+
this.logger.debug("Context messages", {
|
|
1432
|
+
startIndex,
|
|
1433
|
+
endIndex,
|
|
1434
|
+
contextCount: contextMessages.length,
|
|
1435
|
+
contextIds: contextMessages.map((m) => m.id)
|
|
1436
|
+
});
|
|
1437
|
+
includeMessages.push(...contextMessages);
|
|
1438
|
+
} catch (error) {
|
|
1439
|
+
this.logger.warn("Failed to get included message", { messageId: includeItem.id, error });
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
this.logger.debug("Total included messages", {
|
|
1443
|
+
count: includeMessages.length,
|
|
1444
|
+
ids: includeMessages.map((m) => m.id)
|
|
1445
|
+
});
|
|
1446
|
+
return includeMessages;
|
|
1447
|
+
}
|
|
1448
|
+
async updateMessages(args) {
|
|
1449
|
+
const { messages } = args;
|
|
1450
|
+
this.logger.debug("Updating messages", { count: messages.length });
|
|
1451
|
+
if (!messages.length) {
|
|
1452
|
+
return [];
|
|
1453
|
+
}
|
|
1454
|
+
const updatedMessages = [];
|
|
1455
|
+
const affectedThreadIds = /* @__PURE__ */ new Set();
|
|
1456
|
+
try {
|
|
1457
|
+
for (const updateData of messages) {
|
|
1458
|
+
const { id, ...updates } = updateData;
|
|
1459
|
+
const existingMessage = await this.service.entities.message.get({ entity: "message", id }).go();
|
|
1460
|
+
if (!existingMessage.data) {
|
|
1461
|
+
this.logger.warn("Message not found for update", { id });
|
|
1462
|
+
continue;
|
|
1463
|
+
}
|
|
1464
|
+
const existingMsg = this.parseMessageData(existingMessage.data);
|
|
1465
|
+
const originalThreadId = existingMsg.threadId;
|
|
1466
|
+
affectedThreadIds.add(originalThreadId);
|
|
1467
|
+
const updatePayload = {
|
|
1468
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1469
|
+
};
|
|
1470
|
+
if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
|
|
1471
|
+
if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
|
|
1472
|
+
if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
|
|
1473
|
+
if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
|
|
1474
|
+
updatePayload.threadId = updates.threadId;
|
|
1475
|
+
affectedThreadIds.add(updates.threadId);
|
|
1476
|
+
}
|
|
1477
|
+
if (updates.content) {
|
|
1478
|
+
const existingContent = existingMsg.content;
|
|
1479
|
+
let newContent = { ...existingContent };
|
|
1480
|
+
if (updates.content.metadata !== void 0) {
|
|
1481
|
+
newContent.metadata = {
|
|
1482
|
+
...existingContent.metadata || {},
|
|
1483
|
+
...updates.content.metadata || {}
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
if (updates.content.content !== void 0) {
|
|
1487
|
+
newContent.content = updates.content.content;
|
|
1488
|
+
}
|
|
1489
|
+
if ("parts" in updates.content && updates.content.parts !== void 0) {
|
|
1490
|
+
newContent.parts = updates.content.parts;
|
|
1491
|
+
}
|
|
1492
|
+
updatePayload.content = JSON.stringify(newContent);
|
|
1493
|
+
}
|
|
1494
|
+
await this.service.entities.message.update({ entity: "message", id }).set(updatePayload).go();
|
|
1495
|
+
const updatedMessage = await this.service.entities.message.get({ entity: "message", id }).go();
|
|
1496
|
+
if (updatedMessage.data) {
|
|
1497
|
+
updatedMessages.push(this.parseMessageData(updatedMessage.data));
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
for (const threadId of affectedThreadIds) {
|
|
1501
|
+
await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
|
|
1502
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1503
|
+
}).go();
|
|
1504
|
+
}
|
|
1505
|
+
return updatedMessages;
|
|
1506
|
+
} catch (error) {
|
|
1507
|
+
throw new MastraError(
|
|
1508
|
+
{
|
|
1509
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_MESSAGES_FAILED",
|
|
1510
|
+
domain: ErrorDomain.STORAGE,
|
|
1511
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1512
|
+
details: { count: messages.length }
|
|
1513
|
+
},
|
|
1514
|
+
error
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
async getResourceById({ resourceId }) {
|
|
1519
|
+
this.logger.debug("Getting resource by ID", { resourceId });
|
|
1520
|
+
try {
|
|
1521
|
+
const result = await this.service.entities.resource.get({ entity: "resource", id: resourceId }).go();
|
|
1522
|
+
if (!result.data) {
|
|
1523
|
+
return null;
|
|
1524
|
+
}
|
|
1525
|
+
const data = result.data;
|
|
1526
|
+
return {
|
|
1527
|
+
...data,
|
|
1528
|
+
// Convert date strings back to Date objects for consistency
|
|
1529
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
1530
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt,
|
|
1531
|
+
// Ensure workingMemory is always returned as a string, regardless of automatic parsing
|
|
1532
|
+
workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory
|
|
1533
|
+
// metadata is already transformed by the entity's getter
|
|
1534
|
+
};
|
|
1535
|
+
} catch (error) {
|
|
1536
|
+
throw new MastraError(
|
|
1537
|
+
{
|
|
1538
|
+
id: "STORAGE_DYNAMODB_STORE_GET_RESOURCE_BY_ID_FAILED",
|
|
1539
|
+
domain: ErrorDomain.STORAGE,
|
|
1540
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1541
|
+
details: { resourceId }
|
|
1542
|
+
},
|
|
1543
|
+
error
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
async saveResource({ resource }) {
|
|
1548
|
+
this.logger.debug("Saving resource", { resourceId: resource.id });
|
|
1549
|
+
const now = /* @__PURE__ */ new Date();
|
|
1550
|
+
const resourceData = {
|
|
1551
|
+
entity: "resource",
|
|
1552
|
+
id: resource.id,
|
|
1553
|
+
workingMemory: resource.workingMemory,
|
|
1554
|
+
metadata: resource.metadata ? JSON.stringify(resource.metadata) : void 0,
|
|
1555
|
+
createdAt: resource.createdAt?.toISOString() || now.toISOString(),
|
|
1556
|
+
updatedAt: now.toISOString()
|
|
1557
|
+
};
|
|
1558
|
+
try {
|
|
1559
|
+
await this.service.entities.resource.upsert(resourceData).go();
|
|
1560
|
+
return {
|
|
1561
|
+
id: resource.id,
|
|
1562
|
+
workingMemory: resource.workingMemory,
|
|
1563
|
+
metadata: resource.metadata,
|
|
1564
|
+
createdAt: resource.createdAt || now,
|
|
1565
|
+
updatedAt: now
|
|
1566
|
+
};
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
throw new MastraError(
|
|
1569
|
+
{
|
|
1570
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_RESOURCE_FAILED",
|
|
1571
|
+
domain: ErrorDomain.STORAGE,
|
|
1572
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1573
|
+
details: { resourceId: resource.id }
|
|
624
1574
|
},
|
|
625
1575
|
error
|
|
626
1576
|
);
|
|
627
1577
|
}
|
|
628
1578
|
}
|
|
1579
|
+
async updateResource({
|
|
1580
|
+
resourceId,
|
|
1581
|
+
workingMemory,
|
|
1582
|
+
metadata
|
|
1583
|
+
}) {
|
|
1584
|
+
this.logger.debug("Updating resource", { resourceId });
|
|
1585
|
+
try {
|
|
1586
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1587
|
+
if (!existingResource) {
|
|
1588
|
+
const newResource = {
|
|
1589
|
+
id: resourceId,
|
|
1590
|
+
workingMemory,
|
|
1591
|
+
metadata: metadata || {},
|
|
1592
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1593
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1594
|
+
};
|
|
1595
|
+
return this.saveResource({ resource: newResource });
|
|
1596
|
+
}
|
|
1597
|
+
const now = /* @__PURE__ */ new Date();
|
|
1598
|
+
const updateData = {
|
|
1599
|
+
updatedAt: now.toISOString()
|
|
1600
|
+
};
|
|
1601
|
+
if (workingMemory !== void 0) {
|
|
1602
|
+
updateData.workingMemory = workingMemory;
|
|
1603
|
+
}
|
|
1604
|
+
if (metadata) {
|
|
1605
|
+
const existingMetadata = existingResource.metadata || {};
|
|
1606
|
+
const mergedMetadata = { ...existingMetadata, ...metadata };
|
|
1607
|
+
updateData.metadata = JSON.stringify(mergedMetadata);
|
|
1608
|
+
}
|
|
1609
|
+
await this.service.entities.resource.update({ entity: "resource", id: resourceId }).set(updateData).go();
|
|
1610
|
+
return {
|
|
1611
|
+
...existingResource,
|
|
1612
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1613
|
+
metadata: metadata ? { ...existingResource.metadata, ...metadata } : existingResource.metadata,
|
|
1614
|
+
updatedAt: now
|
|
1615
|
+
};
|
|
1616
|
+
} catch (error) {
|
|
1617
|
+
throw new MastraError(
|
|
1618
|
+
{
|
|
1619
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_RESOURCE_FAILED",
|
|
1620
|
+
domain: ErrorDomain.STORAGE,
|
|
1621
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1622
|
+
details: { resourceId }
|
|
1623
|
+
},
|
|
1624
|
+
error
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
var StoreOperationsDynamoDB = class extends StoreOperations {
|
|
1630
|
+
client;
|
|
1631
|
+
tableName;
|
|
1632
|
+
service;
|
|
1633
|
+
constructor({
|
|
1634
|
+
service,
|
|
1635
|
+
tableName,
|
|
1636
|
+
client
|
|
1637
|
+
}) {
|
|
1638
|
+
super();
|
|
1639
|
+
this.service = service;
|
|
1640
|
+
this.client = client;
|
|
1641
|
+
this.tableName = tableName;
|
|
1642
|
+
}
|
|
1643
|
+
async hasColumn() {
|
|
1644
|
+
return true;
|
|
1645
|
+
}
|
|
1646
|
+
async dropTable() {
|
|
1647
|
+
}
|
|
1648
|
+
// Helper methods for entity/table mapping
|
|
1649
|
+
getEntityNameForTable(tableName) {
|
|
1650
|
+
const mapping = {
|
|
1651
|
+
[TABLE_THREADS]: "thread",
|
|
1652
|
+
[TABLE_MESSAGES]: "message",
|
|
1653
|
+
[TABLE_WORKFLOW_SNAPSHOT]: "workflow_snapshot",
|
|
1654
|
+
[TABLE_SCORERS]: "score",
|
|
1655
|
+
[TABLE_TRACES]: "trace",
|
|
1656
|
+
[TABLE_RESOURCES]: "resource",
|
|
1657
|
+
[TABLE_SPANS]: "ai_span"
|
|
1658
|
+
};
|
|
1659
|
+
return mapping[tableName] || null;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Pre-processes a record to ensure Date objects are converted to ISO strings
|
|
1663
|
+
* This is necessary because ElectroDB validation happens before setters are applied
|
|
1664
|
+
*/
|
|
1665
|
+
preprocessRecord(record) {
|
|
1666
|
+
const processed = { ...record };
|
|
1667
|
+
if (processed.createdAt instanceof Date) {
|
|
1668
|
+
processed.createdAt = processed.createdAt.toISOString();
|
|
1669
|
+
}
|
|
1670
|
+
if (processed.updatedAt instanceof Date) {
|
|
1671
|
+
processed.updatedAt = processed.updatedAt.toISOString();
|
|
1672
|
+
}
|
|
1673
|
+
if (processed.created_at instanceof Date) {
|
|
1674
|
+
processed.created_at = processed.created_at.toISOString();
|
|
1675
|
+
}
|
|
1676
|
+
if (processed.result && typeof processed.result === "object") {
|
|
1677
|
+
processed.result = JSON.stringify(processed.result);
|
|
1678
|
+
}
|
|
1679
|
+
if (processed.test_info && typeof processed.test_info === "object") {
|
|
1680
|
+
processed.test_info = JSON.stringify(processed.test_info);
|
|
1681
|
+
} else if (processed.test_info === void 0 || processed.test_info === null) {
|
|
1682
|
+
delete processed.test_info;
|
|
1683
|
+
}
|
|
1684
|
+
if (processed.snapshot && typeof processed.snapshot === "object") {
|
|
1685
|
+
processed.snapshot = JSON.stringify(processed.snapshot);
|
|
1686
|
+
}
|
|
1687
|
+
if (processed.attributes && typeof processed.attributes === "object") {
|
|
1688
|
+
processed.attributes = JSON.stringify(processed.attributes);
|
|
1689
|
+
}
|
|
1690
|
+
if (processed.status && typeof processed.status === "object") {
|
|
1691
|
+
processed.status = JSON.stringify(processed.status);
|
|
1692
|
+
}
|
|
1693
|
+
if (processed.events && typeof processed.events === "object") {
|
|
1694
|
+
processed.events = JSON.stringify(processed.events);
|
|
1695
|
+
}
|
|
1696
|
+
if (processed.links && typeof processed.links === "object") {
|
|
1697
|
+
processed.links = JSON.stringify(processed.links);
|
|
1698
|
+
}
|
|
1699
|
+
return processed;
|
|
1700
|
+
}
|
|
629
1701
|
/**
|
|
630
1702
|
* Validates that the required DynamoDB table exists and is accessible.
|
|
631
1703
|
* This does not check the table structure - it assumes the table
|
|
@@ -644,7 +1716,40 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
644
1716
|
}
|
|
645
1717
|
throw new MastraError(
|
|
646
1718
|
{
|
|
647
|
-
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
|
|
1719
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
|
|
1720
|
+
domain: ErrorDomain.STORAGE,
|
|
1721
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1722
|
+
details: { tableName: this.tableName }
|
|
1723
|
+
},
|
|
1724
|
+
error
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* This method is modified for DynamoDB with ElectroDB single-table design.
|
|
1730
|
+
* It assumes the table is created and managed externally via CDK/CloudFormation.
|
|
1731
|
+
*
|
|
1732
|
+
* This implementation only validates that the required table exists and is accessible.
|
|
1733
|
+
* No table creation is attempted - we simply check if we can access the table.
|
|
1734
|
+
*/
|
|
1735
|
+
async createTable({ tableName }) {
|
|
1736
|
+
this.logger.debug("Validating access to externally managed table", { tableName, physicalTable: this.tableName });
|
|
1737
|
+
try {
|
|
1738
|
+
const tableExists = await this.validateTableExists();
|
|
1739
|
+
if (!tableExists) {
|
|
1740
|
+
this.logger.error(
|
|
1741
|
+
`Table ${this.tableName} does not exist or is not accessible. It should be created via CDK/CloudFormation.`
|
|
1742
|
+
);
|
|
1743
|
+
throw new Error(
|
|
1744
|
+
`Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
this.logger.debug(`Table ${this.tableName} exists and is accessible`);
|
|
1748
|
+
} catch (error) {
|
|
1749
|
+
this.logger.error("Error validating table access", { tableName: this.tableName, error });
|
|
1750
|
+
throw new MastraError(
|
|
1751
|
+
{
|
|
1752
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED",
|
|
648
1753
|
domain: ErrorDomain.STORAGE,
|
|
649
1754
|
category: ErrorCategory.THIRD_PARTY,
|
|
650
1755
|
details: { tableName: this.tableName }
|
|
@@ -653,63 +1758,33 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
653
1758
|
);
|
|
654
1759
|
}
|
|
655
1760
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
1761
|
+
async insert({ tableName, record }) {
|
|
1762
|
+
this.logger.debug("DynamoDB insert called", { tableName });
|
|
1763
|
+
const entityName = this.getEntityNameForTable(tableName);
|
|
1764
|
+
if (!entityName || !this.service.entities[entityName]) {
|
|
1765
|
+
throw new MastraError({
|
|
1766
|
+
id: "STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS",
|
|
1767
|
+
domain: ErrorDomain.STORAGE,
|
|
1768
|
+
category: ErrorCategory.USER,
|
|
1769
|
+
text: "No entity defined for tableName",
|
|
1770
|
+
details: { tableName }
|
|
1771
|
+
});
|
|
664
1772
|
}
|
|
665
1773
|
try {
|
|
666
|
-
|
|
1774
|
+
const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
|
|
1775
|
+
await this.service.entities[entityName].create(dataToSave).go();
|
|
667
1776
|
} catch (error) {
|
|
668
1777
|
throw new MastraError(
|
|
669
1778
|
{
|
|
670
|
-
id: "
|
|
1779
|
+
id: "STORAGE_DYNAMODB_STORE_INSERT_FAILED",
|
|
671
1780
|
domain: ErrorDomain.STORAGE,
|
|
672
1781
|
category: ErrorCategory.THIRD_PARTY,
|
|
673
|
-
details: { tableName
|
|
1782
|
+
details: { tableName }
|
|
674
1783
|
},
|
|
675
1784
|
error
|
|
676
1785
|
);
|
|
677
1786
|
}
|
|
678
1787
|
}
|
|
679
|
-
/**
|
|
680
|
-
* Performs the actual table validation and stores the promise.
|
|
681
|
-
* Handles resetting the stored promise on failure to allow retries.
|
|
682
|
-
*/
|
|
683
|
-
_performInitializationAndStore() {
|
|
684
|
-
return this.validateTableExists().then((exists) => {
|
|
685
|
-
if (!exists) {
|
|
686
|
-
throw new Error(
|
|
687
|
-
`Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
return true;
|
|
691
|
-
}).catch((err) => {
|
|
692
|
-
this.hasInitialized = null;
|
|
693
|
-
throw err;
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
/**
|
|
697
|
-
* Pre-processes a record to ensure Date objects are converted to ISO strings
|
|
698
|
-
* This is necessary because ElectroDB validation happens before setters are applied
|
|
699
|
-
*/
|
|
700
|
-
preprocessRecord(record) {
|
|
701
|
-
const processed = { ...record };
|
|
702
|
-
if (processed.createdAt instanceof Date) {
|
|
703
|
-
processed.createdAt = processed.createdAt.toISOString();
|
|
704
|
-
}
|
|
705
|
-
if (processed.updatedAt instanceof Date) {
|
|
706
|
-
processed.updatedAt = processed.updatedAt.toISOString();
|
|
707
|
-
}
|
|
708
|
-
if (processed.created_at instanceof Date) {
|
|
709
|
-
processed.created_at = processed.created_at.toISOString();
|
|
710
|
-
}
|
|
711
|
-
return processed;
|
|
712
|
-
}
|
|
713
1788
|
async alterTable(_args) {
|
|
714
1789
|
}
|
|
715
1790
|
/**
|
|
@@ -745,10 +1820,10 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
745
1820
|
if (!item.id) throw new Error(`Missing required key 'id' for entity 'message'`);
|
|
746
1821
|
key.id = item.id;
|
|
747
1822
|
break;
|
|
748
|
-
case "
|
|
1823
|
+
case "workflow_snapshot":
|
|
749
1824
|
if (!item.workflow_name)
|
|
750
|
-
throw new Error(`Missing required key 'workflow_name' for entity '
|
|
751
|
-
if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity '
|
|
1825
|
+
throw new Error(`Missing required key 'workflow_name' for entity 'workflow_snapshot'`);
|
|
1826
|
+
if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflow_snapshot'`);
|
|
752
1827
|
key.workflow_name = item.workflow_name;
|
|
753
1828
|
key.run_id = item.run_id;
|
|
754
1829
|
break;
|
|
@@ -760,6 +1835,14 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
760
1835
|
if (!item.id) throw new Error(`Missing required key 'id' for entity 'trace'`);
|
|
761
1836
|
key.id = item.id;
|
|
762
1837
|
break;
|
|
1838
|
+
case "score":
|
|
1839
|
+
if (!item.id) throw new Error(`Missing required key 'id' for entity 'score'`);
|
|
1840
|
+
key.id = item.id;
|
|
1841
|
+
break;
|
|
1842
|
+
case "resource":
|
|
1843
|
+
if (!item.id) throw new Error(`Missing required key 'id' for entity 'resource'`);
|
|
1844
|
+
key.id = item.id;
|
|
1845
|
+
break;
|
|
763
1846
|
default:
|
|
764
1847
|
this.logger.warn(`Unknown entity type encountered during clearTable: ${entityName}`);
|
|
765
1848
|
throw new Error(`Cannot construct delete key for unknown entity type: ${entityName}`);
|
|
@@ -784,46 +1867,10 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
784
1867
|
);
|
|
785
1868
|
}
|
|
786
1869
|
}
|
|
787
|
-
/**
|
|
788
|
-
* Insert a record into the specified "table" (entity)
|
|
789
|
-
*/
|
|
790
|
-
async insert({
|
|
791
|
-
tableName,
|
|
792
|
-
record
|
|
793
|
-
}) {
|
|
794
|
-
this.logger.debug("DynamoDB insert called", { tableName });
|
|
795
|
-
const entityName = this.getEntityNameForTable(tableName);
|
|
796
|
-
if (!entityName || !this.service.entities[entityName]) {
|
|
797
|
-
throw new MastraError({
|
|
798
|
-
id: "STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS",
|
|
799
|
-
domain: ErrorDomain.STORAGE,
|
|
800
|
-
category: ErrorCategory.USER,
|
|
801
|
-
text: "No entity defined for tableName",
|
|
802
|
-
details: { tableName }
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
try {
|
|
806
|
-
const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
|
|
807
|
-
await this.service.entities[entityName].create(dataToSave).go();
|
|
808
|
-
} catch (error) {
|
|
809
|
-
throw new MastraError(
|
|
810
|
-
{
|
|
811
|
-
id: "STORAGE_DYNAMODB_STORE_INSERT_FAILED",
|
|
812
|
-
domain: ErrorDomain.STORAGE,
|
|
813
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
814
|
-
details: { tableName }
|
|
815
|
-
},
|
|
816
|
-
error
|
|
817
|
-
);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
1870
|
/**
|
|
821
1871
|
* Insert multiple records as a batch
|
|
822
1872
|
*/
|
|
823
|
-
async batchInsert({
|
|
824
|
-
tableName,
|
|
825
|
-
records
|
|
826
|
-
}) {
|
|
1873
|
+
async batchInsert({ tableName, records }) {
|
|
827
1874
|
this.logger.debug("DynamoDB batchInsert called", { tableName, count: records.length });
|
|
828
1875
|
const entityName = this.getEntityNameForTable(tableName);
|
|
829
1876
|
if (!entityName || !this.service.entities[entityName]) {
|
|
@@ -868,10 +1915,7 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
868
1915
|
/**
|
|
869
1916
|
* Load a record by its keys
|
|
870
1917
|
*/
|
|
871
|
-
async load({
|
|
872
|
-
tableName,
|
|
873
|
-
keys
|
|
874
|
-
}) {
|
|
1918
|
+
async load({ tableName, keys }) {
|
|
875
1919
|
this.logger.debug("DynamoDB load called", { tableName, keys });
|
|
876
1920
|
const entityName = this.getEntityNameForTable(tableName);
|
|
877
1921
|
if (!entityName || !this.service.entities[entityName]) {
|
|
@@ -903,344 +1947,320 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
903
1947
|
);
|
|
904
1948
|
}
|
|
905
1949
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1950
|
+
};
|
|
1951
|
+
var ScoresStorageDynamoDB = class extends ScoresStorage {
|
|
1952
|
+
service;
|
|
1953
|
+
constructor({ service }) {
|
|
1954
|
+
super();
|
|
1955
|
+
this.service = service;
|
|
1956
|
+
}
|
|
1957
|
+
// Helper function to parse score data (handle JSON fields)
|
|
1958
|
+
parseScoreData(data) {
|
|
1959
|
+
return {
|
|
1960
|
+
...data,
|
|
1961
|
+
// Convert date strings back to Date objects for consistency
|
|
1962
|
+
createdAt: data.createdAt ? new Date(data.createdAt) : /* @__PURE__ */ new Date(),
|
|
1963
|
+
updatedAt: data.updatedAt ? new Date(data.updatedAt) : /* @__PURE__ */ new Date()
|
|
1964
|
+
// JSON fields are already transformed by the entity's getters
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
async getScoreById({ id }) {
|
|
1968
|
+
this.logger.debug("Getting score by ID", { id });
|
|
909
1969
|
try {
|
|
910
|
-
const result = await this.service.entities.
|
|
1970
|
+
const result = await this.service.entities.score.get({ entity: "score", id }).go();
|
|
911
1971
|
if (!result.data) {
|
|
912
1972
|
return null;
|
|
913
1973
|
}
|
|
914
|
-
|
|
915
|
-
return {
|
|
916
|
-
...data,
|
|
917
|
-
// Convert date strings back to Date objects for consistency
|
|
918
|
-
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
919
|
-
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
920
|
-
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
921
|
-
// metadata is already transformed by the entity's getter
|
|
922
|
-
};
|
|
1974
|
+
return this.parseScoreData(result.data);
|
|
923
1975
|
} catch (error) {
|
|
924
1976
|
throw new MastraError(
|
|
925
1977
|
{
|
|
926
|
-
id: "
|
|
1978
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORE_BY_ID_FAILED",
|
|
927
1979
|
domain: ErrorDomain.STORAGE,
|
|
928
1980
|
category: ErrorCategory.THIRD_PARTY,
|
|
929
|
-
details: {
|
|
1981
|
+
details: { id }
|
|
930
1982
|
},
|
|
931
1983
|
error
|
|
932
1984
|
);
|
|
933
1985
|
}
|
|
934
1986
|
}
|
|
935
|
-
async
|
|
936
|
-
|
|
1987
|
+
async saveScore(score) {
|
|
1988
|
+
let validatedScore;
|
|
937
1989
|
try {
|
|
938
|
-
|
|
939
|
-
if (!result.data.length) {
|
|
940
|
-
return [];
|
|
941
|
-
}
|
|
942
|
-
return result.data.map((data) => ({
|
|
943
|
-
...data,
|
|
944
|
-
// Convert date strings back to Date objects for consistency
|
|
945
|
-
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
946
|
-
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
947
|
-
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
948
|
-
// metadata is already transformed by the entity's getter
|
|
949
|
-
}));
|
|
1990
|
+
validatedScore = saveScorePayloadSchema.parse(score);
|
|
950
1991
|
} catch (error) {
|
|
951
1992
|
throw new MastraError(
|
|
952
1993
|
{
|
|
953
|
-
id: "
|
|
1994
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
|
|
954
1995
|
domain: ErrorDomain.STORAGE,
|
|
955
|
-
category: ErrorCategory.THIRD_PARTY
|
|
956
|
-
details: { resourceId }
|
|
1996
|
+
category: ErrorCategory.THIRD_PARTY
|
|
957
1997
|
},
|
|
958
1998
|
error
|
|
959
1999
|
);
|
|
960
2000
|
}
|
|
961
|
-
}
|
|
962
|
-
async saveThread({ thread }) {
|
|
963
|
-
this.logger.debug("Saving thread", { threadId: thread.id });
|
|
964
2001
|
const now = /* @__PURE__ */ new Date();
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
2002
|
+
const scoreId = `score-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
2003
|
+
const scoreData = {
|
|
2004
|
+
entity: "score",
|
|
2005
|
+
id: scoreId,
|
|
2006
|
+
scorerId: validatedScore.scorerId,
|
|
2007
|
+
traceId: validatedScore.traceId || "",
|
|
2008
|
+
spanId: validatedScore.spanId || "",
|
|
2009
|
+
runId: validatedScore.runId,
|
|
2010
|
+
scorer: typeof validatedScore.scorer === "string" ? validatedScore.scorer : JSON.stringify(validatedScore.scorer),
|
|
2011
|
+
preprocessStepResult: typeof validatedScore.preprocessStepResult === "string" ? validatedScore.preprocessStepResult : JSON.stringify(validatedScore.preprocessStepResult),
|
|
2012
|
+
analyzeStepResult: typeof validatedScore.analyzeStepResult === "string" ? validatedScore.analyzeStepResult : JSON.stringify(validatedScore.analyzeStepResult),
|
|
2013
|
+
score: validatedScore.score,
|
|
2014
|
+
reason: validatedScore.reason,
|
|
2015
|
+
preprocessPrompt: validatedScore.preprocessPrompt,
|
|
2016
|
+
generateScorePrompt: validatedScore.generateScorePrompt,
|
|
2017
|
+
generateReasonPrompt: validatedScore.generateReasonPrompt,
|
|
2018
|
+
analyzePrompt: validatedScore.analyzePrompt,
|
|
2019
|
+
input: typeof validatedScore.input === "string" ? validatedScore.input : JSON.stringify(validatedScore.input),
|
|
2020
|
+
output: typeof validatedScore.output === "string" ? validatedScore.output : JSON.stringify(validatedScore.output),
|
|
2021
|
+
additionalContext: typeof validatedScore.additionalContext === "string" ? validatedScore.additionalContext : JSON.stringify(validatedScore.additionalContext),
|
|
2022
|
+
requestContext: typeof validatedScore.requestContext === "string" ? validatedScore.requestContext : JSON.stringify(validatedScore.requestContext),
|
|
2023
|
+
entityType: validatedScore.entityType,
|
|
2024
|
+
entityData: typeof validatedScore.entity === "string" ? validatedScore.entity : JSON.stringify(validatedScore.entity),
|
|
2025
|
+
entityId: validatedScore.entityId,
|
|
2026
|
+
source: validatedScore.source,
|
|
2027
|
+
resourceId: validatedScore.resourceId || "",
|
|
2028
|
+
threadId: validatedScore.threadId || "",
|
|
2029
|
+
createdAt: now.toISOString(),
|
|
2030
|
+
updatedAt: now.toISOString()
|
|
973
2031
|
};
|
|
974
2032
|
try {
|
|
975
|
-
await this.service.entities.
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
createdAt: thread.createdAt || now,
|
|
981
|
-
updatedAt: now,
|
|
982
|
-
metadata: thread.metadata
|
|
983
|
-
};
|
|
984
|
-
} catch (error) {
|
|
985
|
-
throw new MastraError(
|
|
986
|
-
{
|
|
987
|
-
id: "STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED",
|
|
988
|
-
domain: ErrorDomain.STORAGE,
|
|
989
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
990
|
-
details: { threadId: thread.id }
|
|
991
|
-
},
|
|
992
|
-
error
|
|
993
|
-
);
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
async updateThread({
|
|
997
|
-
id,
|
|
998
|
-
title,
|
|
999
|
-
metadata
|
|
1000
|
-
}) {
|
|
1001
|
-
this.logger.debug("Updating thread", { threadId: id });
|
|
1002
|
-
try {
|
|
1003
|
-
const existingThread = await this.getThreadById({ threadId: id });
|
|
1004
|
-
if (!existingThread) {
|
|
1005
|
-
throw new Error(`Thread not found: ${id}`);
|
|
1006
|
-
}
|
|
1007
|
-
const now = /* @__PURE__ */ new Date();
|
|
1008
|
-
const updateData = {
|
|
1009
|
-
updatedAt: now.toISOString()
|
|
1010
|
-
};
|
|
1011
|
-
if (title) {
|
|
1012
|
-
updateData.title = title;
|
|
1013
|
-
}
|
|
1014
|
-
if (metadata) {
|
|
1015
|
-
updateData.metadata = JSON.stringify(metadata);
|
|
1016
|
-
}
|
|
1017
|
-
await this.service.entities.thread.update({ entity: "thread", id }).set(updateData).go();
|
|
1018
|
-
return {
|
|
1019
|
-
...existingThread,
|
|
1020
|
-
title: title || existingThread.title,
|
|
1021
|
-
metadata: metadata || existingThread.metadata,
|
|
2033
|
+
await this.service.entities.score.upsert(scoreData).go();
|
|
2034
|
+
const savedScore = {
|
|
2035
|
+
...score,
|
|
2036
|
+
id: scoreId,
|
|
2037
|
+
createdAt: now,
|
|
1022
2038
|
updatedAt: now
|
|
1023
2039
|
};
|
|
2040
|
+
return { score: savedScore };
|
|
1024
2041
|
} catch (error) {
|
|
1025
2042
|
throw new MastraError(
|
|
1026
2043
|
{
|
|
1027
|
-
id: "
|
|
1028
|
-
domain: ErrorDomain.STORAGE,
|
|
1029
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1030
|
-
details: { threadId: id }
|
|
1031
|
-
},
|
|
1032
|
-
error
|
|
1033
|
-
);
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
async deleteThread({ threadId }) {
|
|
1037
|
-
this.logger.debug("Deleting thread", { threadId });
|
|
1038
|
-
try {
|
|
1039
|
-
await this.service.entities.thread.delete({ entity: "thread", id: threadId }).go();
|
|
1040
|
-
} catch (error) {
|
|
1041
|
-
throw new MastraError(
|
|
1042
|
-
{
|
|
1043
|
-
id: "STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED",
|
|
2044
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
|
|
1044
2045
|
domain: ErrorDomain.STORAGE,
|
|
1045
2046
|
category: ErrorCategory.THIRD_PARTY,
|
|
1046
|
-
details: {
|
|
2047
|
+
details: { scorerId: score.scorerId, runId: score.runId }
|
|
1047
2048
|
},
|
|
1048
2049
|
error
|
|
1049
2050
|
);
|
|
1050
2051
|
}
|
|
1051
2052
|
}
|
|
1052
|
-
async
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
2053
|
+
async listScoresByScorerId({
|
|
2054
|
+
scorerId,
|
|
2055
|
+
pagination,
|
|
2056
|
+
entityId,
|
|
2057
|
+
entityType,
|
|
2058
|
+
source
|
|
1057
2059
|
}) {
|
|
1058
|
-
this.logger.debug("Getting messages", { threadId, selectBy });
|
|
1059
2060
|
try {
|
|
1060
|
-
const query = this.service.entities.
|
|
1061
|
-
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
1062
|
-
if (limit !== Number.MAX_SAFE_INTEGER) {
|
|
1063
|
-
const results2 = await query.go({ limit, order: "desc" });
|
|
1064
|
-
const list2 = new MessageList({ threadId, resourceId }).add(
|
|
1065
|
-
results2.data.map((data) => this.parseMessageData(data)),
|
|
1066
|
-
"memory"
|
|
1067
|
-
);
|
|
1068
|
-
if (format === `v2`) return list2.get.all.v2();
|
|
1069
|
-
return list2.get.all.v1();
|
|
1070
|
-
}
|
|
2061
|
+
const query = this.service.entities.score.query.byScorer({ entity: "score", scorerId });
|
|
1071
2062
|
const results = await query.go();
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
if (
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
if (!threadId) {
|
|
1098
|
-
throw new Error("Thread ID is required");
|
|
1099
|
-
}
|
|
1100
|
-
const messagesToSave = messages.map((msg) => {
|
|
1101
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1102
|
-
return {
|
|
1103
|
-
entity: "message",
|
|
1104
|
-
// Add entity type
|
|
1105
|
-
id: msg.id,
|
|
1106
|
-
threadId: msg.threadId,
|
|
1107
|
-
role: msg.role,
|
|
1108
|
-
type: msg.type,
|
|
1109
|
-
resourceId: msg.resourceId,
|
|
1110
|
-
// Ensure complex fields are stringified if not handled by attribute setters
|
|
1111
|
-
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
1112
|
-
toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
|
|
1113
|
-
toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
|
|
1114
|
-
toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
|
|
1115
|
-
createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
|
|
1116
|
-
updatedAt: now
|
|
1117
|
-
// Add updatedAt
|
|
2063
|
+
let allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2064
|
+
if (entityId) {
|
|
2065
|
+
allScores = allScores.filter((score) => score.entityId === entityId);
|
|
2066
|
+
}
|
|
2067
|
+
if (entityType) {
|
|
2068
|
+
allScores = allScores.filter((score) => score.entityType === entityType);
|
|
2069
|
+
}
|
|
2070
|
+
if (source) {
|
|
2071
|
+
allScores = allScores.filter((score) => score.source === source);
|
|
2072
|
+
}
|
|
2073
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2074
|
+
const { page, perPage: perPageInput } = pagination;
|
|
2075
|
+
const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
|
|
2076
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2077
|
+
const total = allScores.length;
|
|
2078
|
+
const end = perPageInput === false ? allScores.length : start + perPage;
|
|
2079
|
+
const paginatedScores = allScores.slice(start, end);
|
|
2080
|
+
return {
|
|
2081
|
+
scores: paginatedScores,
|
|
2082
|
+
pagination: {
|
|
2083
|
+
total,
|
|
2084
|
+
page,
|
|
2085
|
+
perPage: perPageForResponse,
|
|
2086
|
+
hasMore: end < total
|
|
2087
|
+
}
|
|
1118
2088
|
};
|
|
1119
|
-
});
|
|
1120
|
-
try {
|
|
1121
|
-
const batchSize = 25;
|
|
1122
|
-
const batches = [];
|
|
1123
|
-
for (let i = 0; i < messagesToSave.length; i += batchSize) {
|
|
1124
|
-
const batch = messagesToSave.slice(i, i + batchSize);
|
|
1125
|
-
batches.push(batch);
|
|
1126
|
-
}
|
|
1127
|
-
await Promise.all([
|
|
1128
|
-
// Process message batches
|
|
1129
|
-
...batches.map(async (batch) => {
|
|
1130
|
-
for (const messageData of batch) {
|
|
1131
|
-
if (!messageData.entity) {
|
|
1132
|
-
this.logger.error("Missing entity property in message data for create", { messageData });
|
|
1133
|
-
throw new Error("Internal error: Missing entity property during saveMessages");
|
|
1134
|
-
}
|
|
1135
|
-
await this.service.entities.message.put(messageData).go();
|
|
1136
|
-
}
|
|
1137
|
-
}),
|
|
1138
|
-
// Update thread's updatedAt timestamp
|
|
1139
|
-
this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
|
|
1140
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1141
|
-
}).go()
|
|
1142
|
-
]);
|
|
1143
|
-
const list = new MessageList().add(messages, "memory");
|
|
1144
|
-
if (format === `v1`) return list.get.all.v1();
|
|
1145
|
-
return list.get.all.v2();
|
|
1146
2089
|
} catch (error) {
|
|
1147
2090
|
throw new MastraError(
|
|
1148
2091
|
{
|
|
1149
|
-
id: "
|
|
2092
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
|
|
1150
2093
|
domain: ErrorDomain.STORAGE,
|
|
1151
2094
|
category: ErrorCategory.THIRD_PARTY,
|
|
1152
|
-
details: {
|
|
2095
|
+
details: {
|
|
2096
|
+
scorerId: scorerId || "",
|
|
2097
|
+
entityId: entityId || "",
|
|
2098
|
+
entityType: entityType || "",
|
|
2099
|
+
source: source || "",
|
|
2100
|
+
page: pagination.page,
|
|
2101
|
+
perPage: pagination.perPage
|
|
2102
|
+
}
|
|
1153
2103
|
},
|
|
1154
2104
|
error
|
|
1155
2105
|
);
|
|
1156
2106
|
}
|
|
1157
2107
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
1164
|
-
updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0
|
|
1165
|
-
// Other fields like content, toolCallArgs etc. are assumed to be correctly
|
|
1166
|
-
// transformed by the ElectroDB entity getters.
|
|
1167
|
-
};
|
|
1168
|
-
}
|
|
1169
|
-
// Trace operations
|
|
1170
|
-
async getTraces(args) {
|
|
1171
|
-
const { name, scope, page, perPage } = args;
|
|
1172
|
-
this.logger.debug("Getting traces", { name, scope, page, perPage });
|
|
2108
|
+
async listScoresByRunId({
|
|
2109
|
+
runId,
|
|
2110
|
+
pagination
|
|
2111
|
+
}) {
|
|
2112
|
+
this.logger.debug("Getting scores by run ID", { runId, pagination });
|
|
1173
2113
|
try {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
items = results.data;
|
|
1192
|
-
break;
|
|
1193
|
-
}
|
|
1194
|
-
cursor = results.cursor;
|
|
1195
|
-
if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
|
|
1196
|
-
break;
|
|
2114
|
+
const query = this.service.entities.score.query.byRun({ entity: "score", runId });
|
|
2115
|
+
const results = await query.go();
|
|
2116
|
+
const allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2117
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2118
|
+
const { page, perPage: perPageInput } = pagination;
|
|
2119
|
+
const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
|
|
2120
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2121
|
+
const total = allScores.length;
|
|
2122
|
+
const end = perPageInput === false ? allScores.length : start + perPage;
|
|
2123
|
+
const paginatedScores = allScores.slice(start, end);
|
|
2124
|
+
return {
|
|
2125
|
+
scores: paginatedScores,
|
|
2126
|
+
pagination: {
|
|
2127
|
+
total,
|
|
2128
|
+
page,
|
|
2129
|
+
perPage: perPageForResponse,
|
|
2130
|
+
hasMore: end < total
|
|
1197
2131
|
}
|
|
1198
|
-
}
|
|
1199
|
-
return items;
|
|
2132
|
+
};
|
|
1200
2133
|
} catch (error) {
|
|
1201
2134
|
throw new MastraError(
|
|
1202
2135
|
{
|
|
1203
|
-
id: "
|
|
2136
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_RUN_ID_FAILED",
|
|
1204
2137
|
domain: ErrorDomain.STORAGE,
|
|
1205
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2138
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2139
|
+
details: { runId, page: pagination.page, perPage: pagination.perPage }
|
|
1206
2140
|
},
|
|
1207
2141
|
error
|
|
1208
2142
|
);
|
|
1209
2143
|
}
|
|
1210
2144
|
}
|
|
1211
|
-
async
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
2145
|
+
async listScoresByEntityId({
|
|
2146
|
+
entityId,
|
|
2147
|
+
entityType,
|
|
2148
|
+
pagination
|
|
2149
|
+
}) {
|
|
2150
|
+
this.logger.debug("Getting scores by entity ID", { entityId, entityType, pagination });
|
|
2151
|
+
try {
|
|
2152
|
+
const query = this.service.entities.score.query.byEntityData({ entity: "score", entityId });
|
|
2153
|
+
const results = await query.go();
|
|
2154
|
+
let allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2155
|
+
allScores = allScores.filter((score) => score.entityType === entityType);
|
|
2156
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2157
|
+
const { page, perPage: perPageInput } = pagination;
|
|
2158
|
+
const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
|
|
2159
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2160
|
+
const total = allScores.length;
|
|
2161
|
+
const end = perPageInput === false ? allScores.length : start + perPage;
|
|
2162
|
+
const paginatedScores = allScores.slice(start, end);
|
|
2163
|
+
return {
|
|
2164
|
+
scores: paginatedScores,
|
|
2165
|
+
pagination: {
|
|
2166
|
+
total,
|
|
2167
|
+
page,
|
|
2168
|
+
perPage: perPageForResponse,
|
|
2169
|
+
hasMore: end < total
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
} catch (error) {
|
|
2173
|
+
throw new MastraError(
|
|
2174
|
+
{
|
|
2175
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
2176
|
+
domain: ErrorDomain.STORAGE,
|
|
2177
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2178
|
+
details: { entityId, entityType, page: pagination.page, perPage: pagination.perPage }
|
|
2179
|
+
},
|
|
2180
|
+
error
|
|
2181
|
+
);
|
|
1215
2182
|
}
|
|
2183
|
+
}
|
|
2184
|
+
async listScoresBySpan({
|
|
2185
|
+
traceId,
|
|
2186
|
+
spanId,
|
|
2187
|
+
pagination
|
|
2188
|
+
}) {
|
|
2189
|
+
this.logger.debug("Getting scores by span", { traceId, spanId, pagination });
|
|
1216
2190
|
try {
|
|
1217
|
-
const
|
|
1218
|
-
await
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
2191
|
+
const query = this.service.entities.score.query.bySpan({ entity: "score", traceId, spanId });
|
|
2192
|
+
const results = await query.go();
|
|
2193
|
+
const allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2194
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2195
|
+
const { page, perPage: perPageInput } = pagination;
|
|
2196
|
+
const perPage = normalizePerPage(perPageInput, Number.MAX_SAFE_INTEGER);
|
|
2197
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2198
|
+
const total = allScores.length;
|
|
2199
|
+
const end = perPageInput === false ? allScores.length : start + perPage;
|
|
2200
|
+
const paginatedScores = allScores.slice(start, end);
|
|
2201
|
+
return {
|
|
2202
|
+
scores: paginatedScores,
|
|
2203
|
+
pagination: {
|
|
2204
|
+
total,
|
|
2205
|
+
page,
|
|
2206
|
+
perPage: perPageForResponse,
|
|
2207
|
+
hasMore: end < total
|
|
2208
|
+
}
|
|
2209
|
+
};
|
|
1223
2210
|
} catch (error) {
|
|
1224
2211
|
throw new MastraError(
|
|
1225
2212
|
{
|
|
1226
|
-
id: "
|
|
2213
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SPAN_FAILED",
|
|
1227
2214
|
domain: ErrorDomain.STORAGE,
|
|
1228
2215
|
category: ErrorCategory.THIRD_PARTY,
|
|
1229
|
-
details: {
|
|
2216
|
+
details: { traceId, spanId, page: pagination.page, perPage: pagination.perPage }
|
|
1230
2217
|
},
|
|
1231
2218
|
error
|
|
1232
2219
|
);
|
|
1233
2220
|
}
|
|
1234
2221
|
}
|
|
2222
|
+
};
|
|
2223
|
+
function formatWorkflowRun(snapshotData) {
|
|
2224
|
+
return {
|
|
2225
|
+
workflowName: snapshotData.workflow_name,
|
|
2226
|
+
runId: snapshotData.run_id,
|
|
2227
|
+
snapshot: snapshotData.snapshot,
|
|
2228
|
+
createdAt: new Date(snapshotData.createdAt),
|
|
2229
|
+
updatedAt: new Date(snapshotData.updatedAt),
|
|
2230
|
+
resourceId: snapshotData.resourceId
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
|
|
2234
|
+
service;
|
|
2235
|
+
constructor({ service }) {
|
|
2236
|
+
super();
|
|
2237
|
+
this.service = service;
|
|
2238
|
+
}
|
|
2239
|
+
updateWorkflowResults({
|
|
2240
|
+
// workflowName,
|
|
2241
|
+
// runId,
|
|
2242
|
+
// stepId,
|
|
2243
|
+
// result,
|
|
2244
|
+
// requestContext,
|
|
2245
|
+
}) {
|
|
2246
|
+
throw new Error("Method not implemented.");
|
|
2247
|
+
}
|
|
2248
|
+
updateWorkflowState({
|
|
2249
|
+
// workflowName,
|
|
2250
|
+
// runId,
|
|
2251
|
+
// opts,
|
|
2252
|
+
}) {
|
|
2253
|
+
throw new Error("Method not implemented.");
|
|
2254
|
+
}
|
|
1235
2255
|
// Workflow operations
|
|
1236
2256
|
async persistWorkflowSnapshot({
|
|
1237
2257
|
workflowName,
|
|
1238
2258
|
runId,
|
|
2259
|
+
resourceId,
|
|
1239
2260
|
snapshot
|
|
1240
2261
|
}) {
|
|
1241
2262
|
this.logger.debug("Persisting workflow snapshot", { workflowName, runId });
|
|
1242
2263
|
try {
|
|
1243
|
-
const resourceId = "resourceId" in snapshot ? snapshot.resourceId : void 0;
|
|
1244
2264
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1245
2265
|
const data = {
|
|
1246
2266
|
entity: "workflow_snapshot",
|
|
@@ -1248,12 +2268,11 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1248
2268
|
workflow_name: workflowName,
|
|
1249
2269
|
run_id: runId,
|
|
1250
2270
|
snapshot: JSON.stringify(snapshot),
|
|
1251
|
-
// Stringify the snapshot object
|
|
1252
2271
|
createdAt: now,
|
|
1253
2272
|
updatedAt: now,
|
|
1254
2273
|
resourceId
|
|
1255
2274
|
};
|
|
1256
|
-
await this.service.entities.
|
|
2275
|
+
await this.service.entities.workflow_snapshot.upsert(data).go();
|
|
1257
2276
|
} catch (error) {
|
|
1258
2277
|
throw new MastraError(
|
|
1259
2278
|
{
|
|
@@ -1272,7 +2291,7 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1272
2291
|
}) {
|
|
1273
2292
|
this.logger.debug("Loading workflow snapshot", { workflowName, runId });
|
|
1274
2293
|
try {
|
|
1275
|
-
const result = await this.service.entities.
|
|
2294
|
+
const result = await this.service.entities.workflow_snapshot.get({
|
|
1276
2295
|
entity: "workflow_snapshot",
|
|
1277
2296
|
// Add entity type
|
|
1278
2297
|
workflow_name: workflowName,
|
|
@@ -1294,21 +2313,34 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1294
2313
|
);
|
|
1295
2314
|
}
|
|
1296
2315
|
}
|
|
1297
|
-
async
|
|
2316
|
+
async listWorkflowRuns(args) {
|
|
1298
2317
|
this.logger.debug("Getting workflow runs", { args });
|
|
1299
2318
|
try {
|
|
1300
|
-
const
|
|
1301
|
-
const
|
|
2319
|
+
const perPage = args?.perPage !== void 0 ? args.perPage : 10;
|
|
2320
|
+
const page = args?.page !== void 0 ? args.page : 0;
|
|
2321
|
+
if (page < 0) {
|
|
2322
|
+
throw new MastraError(
|
|
2323
|
+
{
|
|
2324
|
+
id: "DYNAMODB_STORE_INVALID_PAGE",
|
|
2325
|
+
domain: ErrorDomain.STORAGE,
|
|
2326
|
+
category: ErrorCategory.USER,
|
|
2327
|
+
details: { page }
|
|
2328
|
+
},
|
|
2329
|
+
new Error("page must be >= 0")
|
|
2330
|
+
);
|
|
2331
|
+
}
|
|
2332
|
+
const normalizedPerPage = normalizePerPage(perPage, 10);
|
|
2333
|
+
const offset = page * normalizedPerPage;
|
|
1302
2334
|
let query;
|
|
1303
2335
|
if (args?.workflowName) {
|
|
1304
|
-
query = this.service.entities.
|
|
2336
|
+
query = this.service.entities.workflow_snapshot.query.primary({
|
|
1305
2337
|
entity: "workflow_snapshot",
|
|
1306
2338
|
// Add entity type
|
|
1307
2339
|
workflow_name: args.workflowName
|
|
1308
2340
|
});
|
|
1309
2341
|
} else {
|
|
1310
2342
|
this.logger.warn("Performing a scan operation on workflow snapshots - consider using a more specific query");
|
|
1311
|
-
query = this.service.entities.
|
|
2343
|
+
query = this.service.entities.workflow_snapshot.scan;
|
|
1312
2344
|
}
|
|
1313
2345
|
const allMatchingSnapshots = [];
|
|
1314
2346
|
let cursor = null;
|
|
@@ -1320,6 +2352,11 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1320
2352
|
});
|
|
1321
2353
|
if (pageResults.data && pageResults.data.length > 0) {
|
|
1322
2354
|
let pageFilteredData = pageResults.data;
|
|
2355
|
+
if (args?.status) {
|
|
2356
|
+
pageFilteredData = pageFilteredData.filter((snapshot) => {
|
|
2357
|
+
return snapshot.snapshot.status === args.status;
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
1323
2360
|
if (args?.fromDate || args?.toDate) {
|
|
1324
2361
|
pageFilteredData = pageFilteredData.filter((snapshot) => {
|
|
1325
2362
|
const createdAt = new Date(snapshot.createdAt);
|
|
@@ -1345,8 +2382,8 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1345
2382
|
return { runs: [], total: 0 };
|
|
1346
2383
|
}
|
|
1347
2384
|
const total = allMatchingSnapshots.length;
|
|
1348
|
-
const paginatedData = allMatchingSnapshots.slice(offset, offset +
|
|
1349
|
-
const runs = paginatedData.map((snapshot) =>
|
|
2385
|
+
const paginatedData = allMatchingSnapshots.slice(offset, offset + normalizedPerPage);
|
|
2386
|
+
const runs = paginatedData.map((snapshot) => formatWorkflowRun(snapshot));
|
|
1350
2387
|
return {
|
|
1351
2388
|
runs,
|
|
1352
2389
|
total
|
|
@@ -1354,7 +2391,7 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1354
2391
|
} catch (error) {
|
|
1355
2392
|
throw new MastraError(
|
|
1356
2393
|
{
|
|
1357
|
-
id: "
|
|
2394
|
+
id: "STORAGE_DYNAMODB_STORE_LIST_WORKFLOW_RUNS_FAILED",
|
|
1358
2395
|
domain: ErrorDomain.STORAGE,
|
|
1359
2396
|
category: ErrorCategory.THIRD_PARTY,
|
|
1360
2397
|
details: { workflowName: args?.workflowName || "", resourceId: args?.resourceId || "" }
|
|
@@ -1369,7 +2406,7 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1369
2406
|
try {
|
|
1370
2407
|
if (workflowName) {
|
|
1371
2408
|
this.logger.debug("WorkflowName provided, using direct GET operation.");
|
|
1372
|
-
const result2 = await this.service.entities.
|
|
2409
|
+
const result2 = await this.service.entities.workflow_snapshot.get({
|
|
1373
2410
|
entity: "workflow_snapshot",
|
|
1374
2411
|
// Entity type for PK
|
|
1375
2412
|
workflow_name: workflowName,
|
|
@@ -1391,7 +2428,7 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1391
2428
|
this.logger.debug(
|
|
1392
2429
|
'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.'
|
|
1393
2430
|
);
|
|
1394
|
-
const result = await this.service.entities.
|
|
2431
|
+
const result = await this.service.entities.workflow_snapshot.query.gsi2({ entity: "workflow_snapshot", run_id: runId }).go();
|
|
1395
2432
|
const matchingRunDbItem = result.data && result.data.length > 0 ? result.data[0] : null;
|
|
1396
2433
|
if (!matchingRunDbItem) {
|
|
1397
2434
|
return null;
|
|
@@ -1417,121 +2454,232 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1417
2454
|
);
|
|
1418
2455
|
}
|
|
1419
2456
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
2457
|
+
};
|
|
2458
|
+
|
|
2459
|
+
// src/storage/index.ts
|
|
2460
|
+
var DynamoDBStore = class extends MastraStorage {
|
|
2461
|
+
tableName;
|
|
2462
|
+
client;
|
|
2463
|
+
service;
|
|
2464
|
+
hasInitialized = null;
|
|
2465
|
+
stores;
|
|
2466
|
+
constructor({ name, config }) {
|
|
2467
|
+
super({ id: config.id, name });
|
|
2468
|
+
try {
|
|
2469
|
+
if (!config.tableName || typeof config.tableName !== "string" || config.tableName.trim() === "") {
|
|
2470
|
+
throw new Error("DynamoDBStore: config.tableName must be provided and cannot be empty.");
|
|
2471
|
+
}
|
|
2472
|
+
if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
|
|
2473
|
+
throw new Error(
|
|
2474
|
+
`DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
|
|
2475
|
+
);
|
|
2476
|
+
}
|
|
2477
|
+
const dynamoClient = new DynamoDBClient({
|
|
2478
|
+
region: config.region || "us-east-1",
|
|
2479
|
+
endpoint: config.endpoint,
|
|
2480
|
+
credentials: config.credentials
|
|
2481
|
+
});
|
|
2482
|
+
this.tableName = config.tableName;
|
|
2483
|
+
this.client = DynamoDBDocumentClient.from(dynamoClient);
|
|
2484
|
+
this.service = getElectroDbService(this.client, this.tableName);
|
|
2485
|
+
const operations = new StoreOperationsDynamoDB({
|
|
2486
|
+
service: this.service,
|
|
2487
|
+
tableName: this.tableName,
|
|
2488
|
+
client: this.client
|
|
2489
|
+
});
|
|
2490
|
+
const workflows = new WorkflowStorageDynamoDB({ service: this.service });
|
|
2491
|
+
const memory = new MemoryStorageDynamoDB({ service: this.service });
|
|
2492
|
+
const scores = new ScoresStorageDynamoDB({ service: this.service });
|
|
2493
|
+
this.stores = {
|
|
2494
|
+
operations,
|
|
2495
|
+
workflows,
|
|
2496
|
+
memory,
|
|
2497
|
+
scores
|
|
2498
|
+
};
|
|
2499
|
+
} catch (error) {
|
|
2500
|
+
throw new MastraError(
|
|
2501
|
+
{
|
|
2502
|
+
id: "STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED",
|
|
2503
|
+
domain: ErrorDomain.STORAGE,
|
|
2504
|
+
category: ErrorCategory.USER
|
|
2505
|
+
},
|
|
2506
|
+
error
|
|
2507
|
+
);
|
|
2508
|
+
}
|
|
1430
2509
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
2510
|
+
get supports() {
|
|
2511
|
+
return {
|
|
2512
|
+
selectByIncludeResourceScope: true,
|
|
2513
|
+
resourceWorkingMemory: true,
|
|
2514
|
+
hasColumn: false,
|
|
2515
|
+
createTable: false,
|
|
2516
|
+
deleteMessages: false,
|
|
2517
|
+
listScoresBySpan: true
|
|
1439
2518
|
};
|
|
1440
|
-
return mapping[tableName] || null;
|
|
1441
2519
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
2520
|
+
/**
|
|
2521
|
+
* Validates that the required DynamoDB table exists and is accessible.
|
|
2522
|
+
* This does not check the table structure - it assumes the table
|
|
2523
|
+
* was created with the correct structure via CDK/CloudFormation.
|
|
2524
|
+
*/
|
|
2525
|
+
async validateTableExists() {
|
|
1445
2526
|
try {
|
|
1446
|
-
const
|
|
1447
|
-
|
|
1448
|
-
if (!results.data.length) {
|
|
1449
|
-
return [];
|
|
1450
|
-
}
|
|
1451
|
-
let filteredData = results.data;
|
|
1452
|
-
if (type) {
|
|
1453
|
-
filteredData = filteredData.filter((evalRecord) => {
|
|
1454
|
-
try {
|
|
1455
|
-
const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
|
|
1456
|
-
if (type === "test" && !testInfo) {
|
|
1457
|
-
return false;
|
|
1458
|
-
}
|
|
1459
|
-
if (type === "live" && testInfo) {
|
|
1460
|
-
return false;
|
|
1461
|
-
}
|
|
1462
|
-
} catch (e) {
|
|
1463
|
-
this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
|
|
1464
|
-
}
|
|
1465
|
-
return true;
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
return filteredData.map((evalRecord) => {
|
|
1469
|
-
try {
|
|
1470
|
-
return {
|
|
1471
|
-
input: evalRecord.input,
|
|
1472
|
-
output: evalRecord.output,
|
|
1473
|
-
// Safely parse result and test_info
|
|
1474
|
-
result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
|
|
1475
|
-
agentName: evalRecord.agent_name,
|
|
1476
|
-
createdAt: evalRecord.created_at,
|
|
1477
|
-
// Keep as string from DDB?
|
|
1478
|
-
metricName: evalRecord.metric_name,
|
|
1479
|
-
instructions: evalRecord.instructions,
|
|
1480
|
-
runId: evalRecord.run_id,
|
|
1481
|
-
globalRunId: evalRecord.global_run_id,
|
|
1482
|
-
testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
|
|
1483
|
-
};
|
|
1484
|
-
} catch (parseError) {
|
|
1485
|
-
this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
|
|
1486
|
-
return {
|
|
1487
|
-
agentName: evalRecord.agent_name,
|
|
1488
|
-
createdAt: evalRecord.created_at,
|
|
1489
|
-
runId: evalRecord.run_id,
|
|
1490
|
-
globalRunId: evalRecord.global_run_id
|
|
1491
|
-
};
|
|
1492
|
-
}
|
|
2527
|
+
const command = new DescribeTableCommand({
|
|
2528
|
+
TableName: this.tableName
|
|
1493
2529
|
});
|
|
2530
|
+
await this.client.send(command);
|
|
2531
|
+
return true;
|
|
1494
2532
|
} catch (error) {
|
|
2533
|
+
if (error.name === "ResourceNotFoundException") {
|
|
2534
|
+
return false;
|
|
2535
|
+
}
|
|
1495
2536
|
throw new MastraError(
|
|
1496
2537
|
{
|
|
1497
|
-
id: "
|
|
2538
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
|
|
1498
2539
|
domain: ErrorDomain.STORAGE,
|
|
1499
2540
|
category: ErrorCategory.THIRD_PARTY,
|
|
1500
|
-
details: {
|
|
2541
|
+
details: { tableName: this.tableName }
|
|
1501
2542
|
},
|
|
1502
2543
|
error
|
|
1503
2544
|
);
|
|
1504
2545
|
}
|
|
1505
2546
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
2547
|
+
/**
|
|
2548
|
+
* Initialize storage, validating the externally managed table is accessible.
|
|
2549
|
+
* For the single-table design, we only validate once that we can access
|
|
2550
|
+
* the table that was created via CDK/CloudFormation.
|
|
2551
|
+
*/
|
|
2552
|
+
async init() {
|
|
2553
|
+
if (this.hasInitialized === null) {
|
|
2554
|
+
this.hasInitialized = this._performInitializationAndStore();
|
|
2555
|
+
}
|
|
2556
|
+
try {
|
|
2557
|
+
await this.hasInitialized;
|
|
2558
|
+
} catch (error) {
|
|
2559
|
+
throw new MastraError(
|
|
2560
|
+
{
|
|
2561
|
+
id: "STORAGE_DYNAMODB_STORE_INIT_FAILED",
|
|
2562
|
+
domain: ErrorDomain.STORAGE,
|
|
2563
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2564
|
+
details: { tableName: this.tableName }
|
|
2565
|
+
},
|
|
2566
|
+
error
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
1515
2569
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
2570
|
+
/**
|
|
2571
|
+
* Performs the actual table validation and stores the promise.
|
|
2572
|
+
* Handles resetting the stored promise on failure to allow retries.
|
|
2573
|
+
*/
|
|
2574
|
+
_performInitializationAndStore() {
|
|
2575
|
+
return this.validateTableExists().then((exists) => {
|
|
2576
|
+
if (!exists) {
|
|
2577
|
+
throw new Error(
|
|
2578
|
+
`Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
return true;
|
|
2582
|
+
}).catch((err) => {
|
|
2583
|
+
this.hasInitialized = null;
|
|
2584
|
+
throw err;
|
|
2585
|
+
});
|
|
1525
2586
|
}
|
|
1526
|
-
async
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
2587
|
+
async createTable({ tableName, schema }) {
|
|
2588
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
2589
|
+
}
|
|
2590
|
+
async alterTable(_args) {
|
|
2591
|
+
return this.stores.operations.alterTable(_args);
|
|
2592
|
+
}
|
|
2593
|
+
async clearTable({ tableName }) {
|
|
2594
|
+
return this.stores.operations.clearTable({ tableName });
|
|
2595
|
+
}
|
|
2596
|
+
async dropTable({ tableName }) {
|
|
2597
|
+
return this.stores.operations.dropTable({ tableName });
|
|
2598
|
+
}
|
|
2599
|
+
async insert({ tableName, record }) {
|
|
2600
|
+
return this.stores.operations.insert({ tableName, record });
|
|
2601
|
+
}
|
|
2602
|
+
async batchInsert({ tableName, records }) {
|
|
2603
|
+
return this.stores.operations.batchInsert({ tableName, records });
|
|
2604
|
+
}
|
|
2605
|
+
async load({ tableName, keys }) {
|
|
2606
|
+
return this.stores.operations.load({ tableName, keys });
|
|
2607
|
+
}
|
|
2608
|
+
// Thread operations
|
|
2609
|
+
async getThreadById({ threadId }) {
|
|
2610
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
2611
|
+
}
|
|
2612
|
+
async saveThread({ thread }) {
|
|
2613
|
+
return this.stores.memory.saveThread({ thread });
|
|
2614
|
+
}
|
|
2615
|
+
async updateThread({
|
|
2616
|
+
id,
|
|
2617
|
+
title,
|
|
2618
|
+
metadata
|
|
2619
|
+
}) {
|
|
2620
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2621
|
+
}
|
|
2622
|
+
async deleteThread({ threadId }) {
|
|
2623
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
2624
|
+
}
|
|
2625
|
+
async listMessagesById(args) {
|
|
2626
|
+
return this.stores.memory.listMessagesById(args);
|
|
2627
|
+
}
|
|
2628
|
+
async saveMessages(args) {
|
|
2629
|
+
return this.stores.memory.saveMessages(args);
|
|
2630
|
+
}
|
|
2631
|
+
async updateMessages(_args) {
|
|
2632
|
+
return this.stores.memory.updateMessages(_args);
|
|
2633
|
+
}
|
|
2634
|
+
// Workflow operations
|
|
2635
|
+
async updateWorkflowResults({
|
|
2636
|
+
workflowName,
|
|
2637
|
+
runId,
|
|
2638
|
+
stepId,
|
|
2639
|
+
result,
|
|
2640
|
+
requestContext
|
|
2641
|
+
}) {
|
|
2642
|
+
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
|
|
2643
|
+
}
|
|
2644
|
+
async updateWorkflowState({
|
|
2645
|
+
workflowName,
|
|
2646
|
+
runId,
|
|
2647
|
+
opts
|
|
2648
|
+
}) {
|
|
2649
|
+
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
2650
|
+
}
|
|
2651
|
+
async persistWorkflowSnapshot({
|
|
2652
|
+
workflowName,
|
|
2653
|
+
runId,
|
|
2654
|
+
resourceId,
|
|
2655
|
+
snapshot
|
|
2656
|
+
}) {
|
|
2657
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
|
|
2658
|
+
}
|
|
2659
|
+
async loadWorkflowSnapshot({
|
|
2660
|
+
workflowName,
|
|
2661
|
+
runId
|
|
2662
|
+
}) {
|
|
2663
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2664
|
+
}
|
|
2665
|
+
async listWorkflowRuns(args) {
|
|
2666
|
+
return this.stores.workflows.listWorkflowRuns(args);
|
|
2667
|
+
}
|
|
2668
|
+
async getWorkflowRunById(args) {
|
|
2669
|
+
return this.stores.workflows.getWorkflowRunById(args);
|
|
2670
|
+
}
|
|
2671
|
+
async getResourceById({ resourceId }) {
|
|
2672
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
2673
|
+
}
|
|
2674
|
+
async saveResource({ resource }) {
|
|
2675
|
+
return this.stores.memory.saveResource({ resource });
|
|
2676
|
+
}
|
|
2677
|
+
async updateResource({
|
|
2678
|
+
resourceId,
|
|
2679
|
+
workingMemory,
|
|
2680
|
+
metadata
|
|
2681
|
+
}) {
|
|
2682
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
1535
2683
|
}
|
|
1536
2684
|
/**
|
|
1537
2685
|
* Closes the DynamoDB client connection and cleans up resources.
|
|
@@ -1553,10 +2701,50 @@ var DynamoDBStore = class extends MastraStorage {
|
|
|
1553
2701
|
);
|
|
1554
2702
|
}
|
|
1555
2703
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
2704
|
+
/**
|
|
2705
|
+
* SCORERS - Not implemented
|
|
2706
|
+
*/
|
|
2707
|
+
async getScoreById({ id: _id }) {
|
|
2708
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
2709
|
+
}
|
|
2710
|
+
async saveScore(_score) {
|
|
2711
|
+
return this.stores.scores.saveScore(_score);
|
|
2712
|
+
}
|
|
2713
|
+
async listScoresByRunId({
|
|
2714
|
+
runId: _runId,
|
|
2715
|
+
pagination: _pagination
|
|
2716
|
+
}) {
|
|
2717
|
+
return this.stores.scores.listScoresByRunId({ runId: _runId, pagination: _pagination });
|
|
2718
|
+
}
|
|
2719
|
+
async listScoresByEntityId({
|
|
2720
|
+
entityId: _entityId,
|
|
2721
|
+
entityType: _entityType,
|
|
2722
|
+
pagination: _pagination
|
|
2723
|
+
}) {
|
|
2724
|
+
return this.stores.scores.listScoresByEntityId({
|
|
2725
|
+
entityId: _entityId,
|
|
2726
|
+
entityType: _entityType,
|
|
2727
|
+
pagination: _pagination
|
|
2728
|
+
});
|
|
2729
|
+
}
|
|
2730
|
+
async listScoresByScorerId({
|
|
2731
|
+
scorerId,
|
|
2732
|
+
source,
|
|
2733
|
+
entityId,
|
|
2734
|
+
entityType,
|
|
2735
|
+
pagination
|
|
2736
|
+
}) {
|
|
2737
|
+
return this.stores.scores.listScoresByScorerId({ scorerId, source, entityId, entityType, pagination });
|
|
2738
|
+
}
|
|
2739
|
+
async listScoresBySpan({
|
|
2740
|
+
traceId,
|
|
2741
|
+
spanId,
|
|
2742
|
+
pagination
|
|
2743
|
+
}) {
|
|
2744
|
+
return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
|
|
1559
2745
|
}
|
|
1560
2746
|
};
|
|
1561
2747
|
|
|
1562
2748
|
export { DynamoDBStore };
|
|
2749
|
+
//# sourceMappingURL=index.js.map
|
|
2750
|
+
//# sourceMappingURL=index.js.map
|