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