@mastra/upstash 0.11.1-alpha.0 → 0.11.1-alpha.1
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +14 -0
- package/dist/index.cjs +589 -194
- package/dist/index.js +578 -183
- package/package.json +2 -2
- package/src/storage/index.ts +496 -187
- package/src/storage/upstash.test.ts +47 -15
- package/src/vector/index.ts +118 -33
package/src/storage/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MessageList } from '@mastra/core/agent';
|
|
2
2
|
import type { MastraMessageContentV2, MastraMessageV2 } from '@mastra/core/agent';
|
|
3
|
+
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
3
4
|
import type { MetricResult, TestInfo } from '@mastra/core/eval';
|
|
4
5
|
import type { StorageThreadType, MastraMessageV1 } from '@mastra/core/memory';
|
|
5
6
|
import {
|
|
@@ -271,7 +272,17 @@ export class UpstashStore extends MastraStorage {
|
|
|
271
272
|
// Transform to EvalRow format
|
|
272
273
|
return filteredEvals.map(record => this.transformEvalRecord(record));
|
|
273
274
|
} catch (error) {
|
|
274
|
-
|
|
275
|
+
const mastraError = new MastraError(
|
|
276
|
+
{
|
|
277
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_EVALS_BY_AGENT_NAME_FAILED',
|
|
278
|
+
domain: ErrorDomain.STORAGE,
|
|
279
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
280
|
+
details: { agentName },
|
|
281
|
+
},
|
|
282
|
+
error,
|
|
283
|
+
);
|
|
284
|
+
this.logger?.trackException(mastraError);
|
|
285
|
+
this.logger.error(mastraError.toString());
|
|
275
286
|
return [];
|
|
276
287
|
}
|
|
277
288
|
}
|
|
@@ -286,8 +297,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
286
297
|
end: args.toDate,
|
|
287
298
|
};
|
|
288
299
|
}
|
|
289
|
-
|
|
290
|
-
|
|
300
|
+
try {
|
|
301
|
+
const { traces } = await this.getTracesPaginated(args);
|
|
302
|
+
return traces;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
throw new MastraError(
|
|
305
|
+
{
|
|
306
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_TRACES_FAILED',
|
|
307
|
+
domain: ErrorDomain.STORAGE,
|
|
308
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
309
|
+
},
|
|
310
|
+
error,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
291
313
|
}
|
|
292
314
|
|
|
293
315
|
public async getTracesPaginated(
|
|
@@ -389,7 +411,20 @@ export class UpstashStore extends MastraStorage {
|
|
|
389
411
|
hasMore,
|
|
390
412
|
};
|
|
391
413
|
} catch (error) {
|
|
392
|
-
|
|
414
|
+
const mastraError = new MastraError(
|
|
415
|
+
{
|
|
416
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_TRACES_PAGINATED_FAILED',
|
|
417
|
+
domain: ErrorDomain.STORAGE,
|
|
418
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
419
|
+
details: {
|
|
420
|
+
name: args.name || '',
|
|
421
|
+
scope: args.scope || '',
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
error,
|
|
425
|
+
);
|
|
426
|
+
this.logger?.trackException(mastraError);
|
|
427
|
+
this.logger.error(mastraError.toString());
|
|
393
428
|
return {
|
|
394
429
|
traces: [],
|
|
395
430
|
total: 0,
|
|
@@ -409,7 +444,21 @@ export class UpstashStore extends MastraStorage {
|
|
|
409
444
|
}): Promise<void> {
|
|
410
445
|
// Redis is schemaless, so we don't need to create tables
|
|
411
446
|
// But we can store the schema for reference
|
|
412
|
-
|
|
447
|
+
try {
|
|
448
|
+
await this.redis.set(`schema:${tableName}`, schema);
|
|
449
|
+
} catch (error) {
|
|
450
|
+
throw new MastraError(
|
|
451
|
+
{
|
|
452
|
+
id: 'STORAGE_UPSTASH_STORAGE_CREATE_TABLE_FAILED',
|
|
453
|
+
domain: ErrorDomain.STORAGE,
|
|
454
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
455
|
+
details: {
|
|
456
|
+
tableName,
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
error,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
413
462
|
}
|
|
414
463
|
|
|
415
464
|
/**
|
|
@@ -428,13 +477,41 @@ export class UpstashStore extends MastraStorage {
|
|
|
428
477
|
|
|
429
478
|
async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
430
479
|
const pattern = `${tableName}:*`;
|
|
431
|
-
|
|
480
|
+
try {
|
|
481
|
+
await this.scanAndDelete(pattern);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
throw new MastraError(
|
|
484
|
+
{
|
|
485
|
+
id: 'STORAGE_UPSTASH_STORAGE_CLEAR_TABLE_FAILED',
|
|
486
|
+
domain: ErrorDomain.STORAGE,
|
|
487
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
488
|
+
details: {
|
|
489
|
+
tableName,
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
error,
|
|
493
|
+
);
|
|
494
|
+
}
|
|
432
495
|
}
|
|
433
496
|
|
|
434
497
|
async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
|
|
435
498
|
const { key, processedRecord } = this.processRecord(tableName, record);
|
|
436
499
|
|
|
437
|
-
|
|
500
|
+
try {
|
|
501
|
+
await this.redis.set(key, processedRecord);
|
|
502
|
+
} catch (error) {
|
|
503
|
+
throw new MastraError(
|
|
504
|
+
{
|
|
505
|
+
id: 'STORAGE_UPSTASH_STORAGE_INSERT_FAILED',
|
|
506
|
+
domain: ErrorDomain.STORAGE,
|
|
507
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
508
|
+
details: {
|
|
509
|
+
tableName,
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
error,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
438
515
|
}
|
|
439
516
|
|
|
440
517
|
async batchInsert(input: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
@@ -442,37 +519,79 @@ export class UpstashStore extends MastraStorage {
|
|
|
442
519
|
if (!records.length) return;
|
|
443
520
|
|
|
444
521
|
const batchSize = 1000;
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
|
|
522
|
+
try {
|
|
523
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
524
|
+
const batch = records.slice(i, i + batchSize);
|
|
525
|
+
const pipeline = this.redis.pipeline();
|
|
526
|
+
for (const record of batch) {
|
|
527
|
+
const { key, processedRecord } = this.processRecord(tableName, record);
|
|
528
|
+
pipeline.set(key, processedRecord);
|
|
529
|
+
}
|
|
530
|
+
await pipeline.exec();
|
|
451
531
|
}
|
|
452
|
-
|
|
532
|
+
} catch (error) {
|
|
533
|
+
throw new MastraError(
|
|
534
|
+
{
|
|
535
|
+
id: 'STORAGE_UPSTASH_STORAGE_BATCH_INSERT_FAILED',
|
|
536
|
+
domain: ErrorDomain.STORAGE,
|
|
537
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
538
|
+
details: {
|
|
539
|
+
tableName,
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
error,
|
|
543
|
+
);
|
|
453
544
|
}
|
|
454
545
|
}
|
|
455
546
|
|
|
456
547
|
async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
|
|
457
548
|
const key = this.getKey(tableName, keys);
|
|
458
|
-
|
|
459
|
-
|
|
549
|
+
try {
|
|
550
|
+
const data = await this.redis.get<R>(key);
|
|
551
|
+
return data || null;
|
|
552
|
+
} catch (error) {
|
|
553
|
+
throw new MastraError(
|
|
554
|
+
{
|
|
555
|
+
id: 'STORAGE_UPSTASH_STORAGE_LOAD_FAILED',
|
|
556
|
+
domain: ErrorDomain.STORAGE,
|
|
557
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
558
|
+
details: {
|
|
559
|
+
tableName,
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
error,
|
|
563
|
+
);
|
|
564
|
+
}
|
|
460
565
|
}
|
|
461
566
|
|
|
462
567
|
async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
568
|
+
try {
|
|
569
|
+
const thread = await this.load<StorageThreadType>({
|
|
570
|
+
tableName: TABLE_THREADS,
|
|
571
|
+
keys: { id: threadId },
|
|
572
|
+
});
|
|
467
573
|
|
|
468
|
-
|
|
574
|
+
if (!thread) return null;
|
|
469
575
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
576
|
+
return {
|
|
577
|
+
...thread,
|
|
578
|
+
createdAt: this.ensureDate(thread.createdAt)!,
|
|
579
|
+
updatedAt: this.ensureDate(thread.updatedAt)!,
|
|
580
|
+
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
581
|
+
};
|
|
582
|
+
} catch (error) {
|
|
583
|
+
throw new MastraError(
|
|
584
|
+
{
|
|
585
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_THREAD_BY_ID_FAILED',
|
|
586
|
+
domain: ErrorDomain.STORAGE,
|
|
587
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
588
|
+
details: {
|
|
589
|
+
threadId,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
error,
|
|
593
|
+
);
|
|
594
|
+
}
|
|
476
595
|
}
|
|
477
596
|
|
|
478
597
|
/**
|
|
@@ -507,7 +626,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
507
626
|
allThreads.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
508
627
|
return allThreads;
|
|
509
628
|
} catch (error) {
|
|
510
|
-
|
|
629
|
+
const mastraError = new MastraError(
|
|
630
|
+
{
|
|
631
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED',
|
|
632
|
+
domain: ErrorDomain.STORAGE,
|
|
633
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
634
|
+
details: {
|
|
635
|
+
resourceId,
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
error,
|
|
639
|
+
);
|
|
640
|
+
this.logger?.trackException(mastraError);
|
|
641
|
+
this.logger.error(mastraError.toString());
|
|
511
642
|
return [];
|
|
512
643
|
}
|
|
513
644
|
}
|
|
@@ -536,7 +667,21 @@ export class UpstashStore extends MastraStorage {
|
|
|
536
667
|
hasMore,
|
|
537
668
|
};
|
|
538
669
|
} catch (error) {
|
|
539
|
-
|
|
670
|
+
const mastraError = new MastraError(
|
|
671
|
+
{
|
|
672
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED',
|
|
673
|
+
domain: ErrorDomain.STORAGE,
|
|
674
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
675
|
+
details: {
|
|
676
|
+
resourceId,
|
|
677
|
+
page,
|
|
678
|
+
perPage,
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
error,
|
|
682
|
+
);
|
|
683
|
+
this.logger?.trackException(mastraError);
|
|
684
|
+
this.logger.error(mastraError.toString());
|
|
540
685
|
return {
|
|
541
686
|
threads: [],
|
|
542
687
|
total: 0,
|
|
@@ -548,11 +693,28 @@ export class UpstashStore extends MastraStorage {
|
|
|
548
693
|
}
|
|
549
694
|
|
|
550
695
|
async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
696
|
+
try {
|
|
697
|
+
await this.insert({
|
|
698
|
+
tableName: TABLE_THREADS,
|
|
699
|
+
record: thread,
|
|
700
|
+
});
|
|
701
|
+
return thread;
|
|
702
|
+
} catch (error) {
|
|
703
|
+
const mastraError = new MastraError(
|
|
704
|
+
{
|
|
705
|
+
id: 'STORAGE_UPSTASH_STORAGE_SAVE_THREAD_FAILED',
|
|
706
|
+
domain: ErrorDomain.STORAGE,
|
|
707
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
708
|
+
details: {
|
|
709
|
+
threadId: thread.id,
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
error,
|
|
713
|
+
);
|
|
714
|
+
this.logger?.trackException(mastraError);
|
|
715
|
+
this.logger.error(mastraError.toString());
|
|
716
|
+
throw mastraError;
|
|
717
|
+
}
|
|
556
718
|
}
|
|
557
719
|
|
|
558
720
|
async updateThread({
|
|
@@ -566,7 +728,15 @@ export class UpstashStore extends MastraStorage {
|
|
|
566
728
|
}): Promise<StorageThreadType> {
|
|
567
729
|
const thread = await this.getThreadById({ threadId: id });
|
|
568
730
|
if (!thread) {
|
|
569
|
-
throw new
|
|
731
|
+
throw new MastraError({
|
|
732
|
+
id: 'STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED',
|
|
733
|
+
domain: ErrorDomain.STORAGE,
|
|
734
|
+
category: ErrorCategory.USER,
|
|
735
|
+
text: `Thread ${id} not found`,
|
|
736
|
+
details: {
|
|
737
|
+
threadId: id,
|
|
738
|
+
},
|
|
739
|
+
});
|
|
570
740
|
}
|
|
571
741
|
|
|
572
742
|
const updatedThread = {
|
|
@@ -578,30 +748,58 @@ export class UpstashStore extends MastraStorage {
|
|
|
578
748
|
},
|
|
579
749
|
};
|
|
580
750
|
|
|
581
|
-
|
|
582
|
-
|
|
751
|
+
try {
|
|
752
|
+
await this.saveThread({ thread: updatedThread });
|
|
753
|
+
return updatedThread;
|
|
754
|
+
} catch (error) {
|
|
755
|
+
throw new MastraError(
|
|
756
|
+
{
|
|
757
|
+
id: 'STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED',
|
|
758
|
+
domain: ErrorDomain.STORAGE,
|
|
759
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
760
|
+
details: {
|
|
761
|
+
threadId: id,
|
|
762
|
+
},
|
|
763
|
+
},
|
|
764
|
+
error,
|
|
765
|
+
);
|
|
766
|
+
}
|
|
583
767
|
}
|
|
584
768
|
|
|
585
769
|
async deleteThread({ threadId }: { threadId: string }): Promise<void> {
|
|
586
770
|
// Delete thread metadata and sorted set
|
|
587
771
|
const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
|
|
588
772
|
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
589
|
-
|
|
773
|
+
try {
|
|
774
|
+
const messageIds: string[] = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
590
775
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
776
|
+
const pipeline = this.redis.pipeline();
|
|
777
|
+
pipeline.del(threadKey);
|
|
778
|
+
pipeline.del(threadMessagesKey);
|
|
594
779
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
780
|
+
for (let i = 0; i < messageIds.length; i++) {
|
|
781
|
+
const messageId = messageIds[i];
|
|
782
|
+
const messageKey = this.getMessageKey(threadId, messageId as string);
|
|
783
|
+
pipeline.del(messageKey);
|
|
784
|
+
}
|
|
600
785
|
|
|
601
|
-
|
|
786
|
+
await pipeline.exec();
|
|
602
787
|
|
|
603
|
-
|
|
604
|
-
|
|
788
|
+
// Bulk delete all message keys for this thread if any remain
|
|
789
|
+
await this.scanAndDelete(this.getMessageKey(threadId, '*'));
|
|
790
|
+
} catch (error) {
|
|
791
|
+
throw new MastraError(
|
|
792
|
+
{
|
|
793
|
+
id: 'STORAGE_UPSTASH_STORAGE_DELETE_THREAD_FAILED',
|
|
794
|
+
domain: ErrorDomain.STORAGE,
|
|
795
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
796
|
+
details: {
|
|
797
|
+
threadId,
|
|
798
|
+
},
|
|
799
|
+
},
|
|
800
|
+
error,
|
|
801
|
+
);
|
|
802
|
+
}
|
|
605
803
|
}
|
|
606
804
|
|
|
607
805
|
async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
|
|
@@ -613,14 +811,25 @@ export class UpstashStore extends MastraStorage {
|
|
|
613
811
|
if (messages.length === 0) return [];
|
|
614
812
|
|
|
615
813
|
const threadId = messages[0]?.threadId;
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
814
|
+
try {
|
|
815
|
+
if (!threadId) {
|
|
816
|
+
throw new Error('Thread ID is required');
|
|
817
|
+
}
|
|
619
818
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
819
|
+
// Check if thread exists
|
|
820
|
+
const thread = await this.getThreadById({ threadId });
|
|
821
|
+
if (!thread) {
|
|
822
|
+
throw new Error(`Thread ${threadId} not found`);
|
|
823
|
+
}
|
|
824
|
+
} catch (error) {
|
|
825
|
+
throw new MastraError(
|
|
826
|
+
{
|
|
827
|
+
id: 'STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_INVALID_ARGS',
|
|
828
|
+
domain: ErrorDomain.STORAGE,
|
|
829
|
+
category: ErrorCategory.USER,
|
|
830
|
+
},
|
|
831
|
+
error,
|
|
832
|
+
);
|
|
624
833
|
}
|
|
625
834
|
|
|
626
835
|
// Add an index to each message to maintain order
|
|
@@ -633,41 +842,55 @@ export class UpstashStore extends MastraStorage {
|
|
|
633
842
|
const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
|
|
634
843
|
const existingThread = await this.redis.get<StorageThreadType>(threadKey);
|
|
635
844
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
845
|
+
try {
|
|
846
|
+
const batchSize = 1000;
|
|
847
|
+
for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
|
|
848
|
+
const batch = messagesWithIndex.slice(i, i + batchSize);
|
|
849
|
+
const pipeline = this.redis.pipeline();
|
|
850
|
+
|
|
851
|
+
for (const message of batch) {
|
|
852
|
+
const key = this.getMessageKey(message.threadId!, message.id);
|
|
853
|
+
const createdAtScore = new Date(message.createdAt).getTime();
|
|
854
|
+
const score = message._index !== undefined ? message._index : createdAtScore;
|
|
855
|
+
|
|
856
|
+
// Store the message data
|
|
857
|
+
pipeline.set(key, message);
|
|
858
|
+
|
|
859
|
+
// Add to sorted set for this thread
|
|
860
|
+
pipeline.zadd(this.getThreadMessagesKey(message.threadId!), {
|
|
861
|
+
score,
|
|
862
|
+
member: message.id,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
648
865
|
|
|
649
|
-
//
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
866
|
+
// Update the thread's updatedAt field (only in the first batch)
|
|
867
|
+
if (i === 0 && existingThread) {
|
|
868
|
+
const updatedThread = {
|
|
869
|
+
...existingThread,
|
|
870
|
+
updatedAt: new Date(),
|
|
871
|
+
};
|
|
872
|
+
pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
|
|
873
|
+
}
|
|
655
874
|
|
|
656
|
-
|
|
657
|
-
if (i === 0 && existingThread) {
|
|
658
|
-
const updatedThread = {
|
|
659
|
-
...existingThread,
|
|
660
|
-
updatedAt: new Date(),
|
|
661
|
-
};
|
|
662
|
-
pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
|
|
875
|
+
await pipeline.exec();
|
|
663
876
|
}
|
|
664
877
|
|
|
665
|
-
|
|
878
|
+
const list = new MessageList().add(messages, 'memory');
|
|
879
|
+
if (format === `v2`) return list.get.all.v2();
|
|
880
|
+
return list.get.all.v1();
|
|
881
|
+
} catch (error) {
|
|
882
|
+
throw new MastraError(
|
|
883
|
+
{
|
|
884
|
+
id: 'STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_FAILED',
|
|
885
|
+
domain: ErrorDomain.STORAGE,
|
|
886
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
887
|
+
details: {
|
|
888
|
+
threadId,
|
|
889
|
+
},
|
|
890
|
+
},
|
|
891
|
+
error,
|
|
892
|
+
);
|
|
666
893
|
}
|
|
667
|
-
|
|
668
|
-
const list = new MessageList().add(messages, 'memory');
|
|
669
|
-
if (format === `v2`) return list.get.all.v2();
|
|
670
|
-
return list.get.all.v1();
|
|
671
894
|
}
|
|
672
895
|
|
|
673
896
|
private async _getIncludedMessages(
|
|
@@ -734,89 +957,96 @@ export class UpstashStore extends MastraStorage {
|
|
|
734
957
|
format,
|
|
735
958
|
}: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
|
|
736
959
|
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
if (typeof selectBy?.last === 'number') {
|
|
741
|
-
limit = Math.max(0, selectBy.last);
|
|
742
|
-
} else if (selectBy?.last === false) {
|
|
743
|
-
limit = 0;
|
|
744
|
-
} else {
|
|
745
|
-
// No limit specified - get all messages
|
|
746
|
-
limit = Number.MAX_SAFE_INTEGER;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
const messageIds = new Set<string>();
|
|
750
|
-
const messageIdToThreadIds: Record<string, string> = {};
|
|
960
|
+
try {
|
|
961
|
+
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
962
|
+
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
751
963
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
964
|
+
const messageIds = new Set<string>();
|
|
965
|
+
const messageIdToThreadIds: Record<string, string> = {};
|
|
755
966
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
760
|
-
allIds.forEach(id => {
|
|
761
|
-
messageIds.add(id as string);
|
|
762
|
-
messageIdToThreadIds[id as string] = threadId;
|
|
763
|
-
});
|
|
764
|
-
} else if (limit > 0) {
|
|
765
|
-
// Get limited number of recent messages
|
|
766
|
-
const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
|
|
767
|
-
latestIds.forEach(id => {
|
|
768
|
-
messageIds.add(id as string);
|
|
769
|
-
messageIdToThreadIds[id as string] = threadId;
|
|
770
|
-
});
|
|
771
|
-
}
|
|
967
|
+
if (limit === 0 && !selectBy?.include) {
|
|
968
|
+
return [];
|
|
969
|
+
}
|
|
772
970
|
|
|
773
|
-
|
|
971
|
+
// Then get the most recent messages (or all if no limit)
|
|
972
|
+
if (limit === Number.MAX_SAFE_INTEGER) {
|
|
973
|
+
// Get all messages
|
|
974
|
+
const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
975
|
+
allIds.forEach(id => {
|
|
976
|
+
messageIds.add(id as string);
|
|
977
|
+
messageIdToThreadIds[id as string] = threadId;
|
|
978
|
+
});
|
|
979
|
+
} else if (limit > 0) {
|
|
980
|
+
// Get limited number of recent messages
|
|
981
|
+
const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
|
|
982
|
+
latestIds.forEach(id => {
|
|
983
|
+
messageIds.add(id as string);
|
|
984
|
+
messageIdToThreadIds[id as string] = threadId;
|
|
985
|
+
});
|
|
986
|
+
}
|
|
774
987
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
988
|
+
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
989
|
+
|
|
990
|
+
// Fetch all needed messages in parallel
|
|
991
|
+
const messages = [
|
|
992
|
+
...includedMessages,
|
|
993
|
+
...((
|
|
994
|
+
await Promise.all(
|
|
995
|
+
Array.from(messageIds).map(async id => {
|
|
996
|
+
const tId = messageIdToThreadIds[id] || threadId;
|
|
997
|
+
const byThreadId = await this.redis.get<MastraMessageV2 & { _index?: number }>(
|
|
998
|
+
this.getMessageKey(tId, id),
|
|
999
|
+
);
|
|
1000
|
+
if (byThreadId) return byThreadId;
|
|
1001
|
+
|
|
1002
|
+
return null;
|
|
1003
|
+
}),
|
|
1004
|
+
)
|
|
1005
|
+
).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
|
|
1006
|
+
];
|
|
784
1007
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
)
|
|
788
|
-
).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
|
|
789
|
-
];
|
|
1008
|
+
// Sort messages by their position in the sorted set
|
|
1009
|
+
messages.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
|
|
790
1010
|
|
|
791
|
-
|
|
792
|
-
|
|
1011
|
+
const seen = new Set<string>();
|
|
1012
|
+
const dedupedMessages = messages.filter(row => {
|
|
1013
|
+
if (seen.has(row.id)) return false;
|
|
1014
|
+
seen.add(row.id);
|
|
1015
|
+
return true;
|
|
1016
|
+
});
|
|
793
1017
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1018
|
+
// Remove _index before returning and handle format conversion properly
|
|
1019
|
+
const prepared = dedupedMessages
|
|
1020
|
+
.filter(message => message !== null && message !== undefined)
|
|
1021
|
+
.map(message => {
|
|
1022
|
+
const { _index, ...messageWithoutIndex } = message as MastraMessageV2 & { _index?: number };
|
|
1023
|
+
return messageWithoutIndex as unknown as MastraMessageV1;
|
|
1024
|
+
});
|
|
800
1025
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1026
|
+
// For backward compatibility, return messages directly without using MessageList
|
|
1027
|
+
// since MessageList has deduplication logic that can cause issues
|
|
1028
|
+
if (format === 'v2') {
|
|
1029
|
+
// Convert V1 format back to V2 format
|
|
1030
|
+
return prepared.map(msg => ({
|
|
1031
|
+
...msg,
|
|
1032
|
+
content: msg.content || { format: 2, parts: [{ type: 'text', text: '' }] },
|
|
1033
|
+
})) as MastraMessageV2[];
|
|
1034
|
+
}
|
|
808
1035
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1036
|
+
return prepared;
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
throw new MastraError(
|
|
1039
|
+
{
|
|
1040
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_MESSAGES_FAILED',
|
|
1041
|
+
domain: ErrorDomain.STORAGE,
|
|
1042
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1043
|
+
details: {
|
|
1044
|
+
threadId,
|
|
1045
|
+
},
|
|
1046
|
+
},
|
|
1047
|
+
error,
|
|
1048
|
+
);
|
|
817
1049
|
}
|
|
818
|
-
|
|
819
|
-
return prepared;
|
|
820
1050
|
}
|
|
821
1051
|
|
|
822
1052
|
public async getMessagesPaginated(
|
|
@@ -831,10 +1061,10 @@ export class UpstashStore extends MastraStorage {
|
|
|
831
1061
|
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
832
1062
|
const messages: (MastraMessageV2 | MastraMessageV1)[] = [];
|
|
833
1063
|
|
|
834
|
-
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
835
|
-
messages.push(...includedMessages);
|
|
836
|
-
|
|
837
1064
|
try {
|
|
1065
|
+
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1066
|
+
messages.push(...includedMessages);
|
|
1067
|
+
|
|
838
1068
|
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
839
1069
|
if (allMessageIds.length === 0) {
|
|
840
1070
|
return {
|
|
@@ -891,7 +1121,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
891
1121
|
hasMore,
|
|
892
1122
|
};
|
|
893
1123
|
} catch (error) {
|
|
894
|
-
|
|
1124
|
+
const mastraError = new MastraError(
|
|
1125
|
+
{
|
|
1126
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_MESSAGES_PAGINATED_FAILED',
|
|
1127
|
+
domain: ErrorDomain.STORAGE,
|
|
1128
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1129
|
+
details: {
|
|
1130
|
+
threadId,
|
|
1131
|
+
},
|
|
1132
|
+
},
|
|
1133
|
+
error,
|
|
1134
|
+
);
|
|
1135
|
+
this.logger.error(mastraError.toString());
|
|
1136
|
+
this.logger?.trackException(mastraError);
|
|
895
1137
|
return {
|
|
896
1138
|
messages: [],
|
|
897
1139
|
total: 0,
|
|
@@ -909,17 +1151,33 @@ export class UpstashStore extends MastraStorage {
|
|
|
909
1151
|
snapshot: WorkflowRunState;
|
|
910
1152
|
}): Promise<void> {
|
|
911
1153
|
const { namespace = 'workflows', workflowName, runId, snapshot } = params;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1154
|
+
try {
|
|
1155
|
+
await this.insert({
|
|
1156
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1157
|
+
record: {
|
|
1158
|
+
namespace,
|
|
1159
|
+
workflow_name: workflowName,
|
|
1160
|
+
run_id: runId,
|
|
1161
|
+
snapshot,
|
|
1162
|
+
createdAt: new Date(),
|
|
1163
|
+
updatedAt: new Date(),
|
|
1164
|
+
},
|
|
1165
|
+
});
|
|
1166
|
+
} catch (error) {
|
|
1167
|
+
throw new MastraError(
|
|
1168
|
+
{
|
|
1169
|
+
id: 'STORAGE_UPSTASH_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED',
|
|
1170
|
+
domain: ErrorDomain.STORAGE,
|
|
1171
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1172
|
+
details: {
|
|
1173
|
+
namespace,
|
|
1174
|
+
workflowName,
|
|
1175
|
+
runId,
|
|
1176
|
+
},
|
|
1177
|
+
},
|
|
1178
|
+
error,
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
923
1181
|
}
|
|
924
1182
|
|
|
925
1183
|
async loadWorkflowSnapshot(params: {
|
|
@@ -933,14 +1191,30 @@ export class UpstashStore extends MastraStorage {
|
|
|
933
1191
|
workflow_name: workflowName,
|
|
934
1192
|
run_id: runId,
|
|
935
1193
|
});
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1194
|
+
try {
|
|
1195
|
+
const data = await this.redis.get<{
|
|
1196
|
+
namespace: string;
|
|
1197
|
+
workflow_name: string;
|
|
1198
|
+
run_id: string;
|
|
1199
|
+
snapshot: WorkflowRunState;
|
|
1200
|
+
}>(key);
|
|
1201
|
+
if (!data) return null;
|
|
1202
|
+
return data.snapshot;
|
|
1203
|
+
} catch (error) {
|
|
1204
|
+
throw new MastraError(
|
|
1205
|
+
{
|
|
1206
|
+
id: 'STORAGE_UPSTASH_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED',
|
|
1207
|
+
domain: ErrorDomain.STORAGE,
|
|
1208
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1209
|
+
details: {
|
|
1210
|
+
namespace,
|
|
1211
|
+
workflowName,
|
|
1212
|
+
runId,
|
|
1213
|
+
},
|
|
1214
|
+
},
|
|
1215
|
+
error,
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
944
1218
|
}
|
|
945
1219
|
|
|
946
1220
|
/**
|
|
@@ -1063,7 +1337,20 @@ export class UpstashStore extends MastraStorage {
|
|
|
1063
1337
|
};
|
|
1064
1338
|
} catch (error) {
|
|
1065
1339
|
const { page = 0, perPage = 100 } = options || {};
|
|
1066
|
-
|
|
1340
|
+
const mastraError = new MastraError(
|
|
1341
|
+
{
|
|
1342
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_EVALS_FAILED',
|
|
1343
|
+
domain: ErrorDomain.STORAGE,
|
|
1344
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1345
|
+
details: {
|
|
1346
|
+
page,
|
|
1347
|
+
perPage,
|
|
1348
|
+
},
|
|
1349
|
+
},
|
|
1350
|
+
error,
|
|
1351
|
+
);
|
|
1352
|
+
this.logger.error(mastraError.toString());
|
|
1353
|
+
this.logger?.trackException(mastraError);
|
|
1067
1354
|
return {
|
|
1068
1355
|
evals: [],
|
|
1069
1356
|
total: 0,
|
|
@@ -1146,8 +1433,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
1146
1433
|
|
|
1147
1434
|
return { runs, total };
|
|
1148
1435
|
} catch (error) {
|
|
1149
|
-
|
|
1150
|
-
|
|
1436
|
+
throw new MastraError(
|
|
1437
|
+
{
|
|
1438
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUNS_FAILED',
|
|
1439
|
+
domain: ErrorDomain.STORAGE,
|
|
1440
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1441
|
+
details: {
|
|
1442
|
+
namespace,
|
|
1443
|
+
workflowName: workflowName || '',
|
|
1444
|
+
resourceId: resourceId || '',
|
|
1445
|
+
},
|
|
1446
|
+
},
|
|
1447
|
+
error,
|
|
1448
|
+
);
|
|
1151
1449
|
}
|
|
1152
1450
|
}
|
|
1153
1451
|
|
|
@@ -1180,8 +1478,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
1180
1478
|
if (!data) return null;
|
|
1181
1479
|
return this.parseWorkflowRun(data);
|
|
1182
1480
|
} catch (error) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1481
|
+
throw new MastraError(
|
|
1482
|
+
{
|
|
1483
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED',
|
|
1484
|
+
domain: ErrorDomain.STORAGE,
|
|
1485
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1486
|
+
details: {
|
|
1487
|
+
namespace,
|
|
1488
|
+
runId,
|
|
1489
|
+
workflowName: workflowName || '',
|
|
1490
|
+
},
|
|
1491
|
+
},
|
|
1492
|
+
error,
|
|
1493
|
+
);
|
|
1185
1494
|
}
|
|
1186
1495
|
}
|
|
1187
1496
|
|