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