@mastra/upstash 0.10.3-alpha.2 → 0.10.3-alpha.3
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 +14 -14
- package/CHANGELOG.md +9 -0
- package/dist/_tsup-dts-rollup.d.cts +22 -51
- package/dist/_tsup-dts-rollup.d.ts +22 -51
- package/dist/index.cjs +306 -358
- package/dist/index.js +306 -358
- package/package.json +3 -3
- package/src/storage/index.ts +270 -363
- package/src/storage/upstash.test.ts +76 -87
package/src/storage/index.ts
CHANGED
|
@@ -15,6 +15,9 @@ import type {
|
|
|
15
15
|
EvalRow,
|
|
16
16
|
WorkflowRuns,
|
|
17
17
|
WorkflowRun,
|
|
18
|
+
PaginationInfo,
|
|
19
|
+
PaginationArgs,
|
|
20
|
+
StorageGetTracesArg,
|
|
18
21
|
} from '@mastra/core/storage';
|
|
19
22
|
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
20
23
|
import { Redis } from '@upstash/redis';
|
|
@@ -272,82 +275,44 @@ export class UpstashStore extends MastraStorage {
|
|
|
272
275
|
}
|
|
273
276
|
}
|
|
274
277
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
page: number;
|
|
289
|
-
perPage?: number;
|
|
290
|
-
attributes?: Record<string, string>;
|
|
291
|
-
filters?: Record<string, any>;
|
|
292
|
-
fromDate?: Date;
|
|
293
|
-
toDate?: Date;
|
|
294
|
-
returnPaginationResults: true;
|
|
295
|
-
}): Promise<{
|
|
296
|
-
traces: any[];
|
|
297
|
-
total: number;
|
|
298
|
-
page: number;
|
|
299
|
-
perPage: number;
|
|
300
|
-
hasMore: boolean;
|
|
301
|
-
}>;
|
|
302
|
-
public async getTraces(args: {
|
|
303
|
-
name?: string;
|
|
304
|
-
scope?: string;
|
|
305
|
-
page: number;
|
|
306
|
-
perPage?: number;
|
|
307
|
-
attributes?: Record<string, string>;
|
|
308
|
-
filters?: Record<string, any>;
|
|
309
|
-
fromDate?: Date;
|
|
310
|
-
toDate?: Date;
|
|
311
|
-
returnPaginationResults?: boolean;
|
|
312
|
-
}): Promise<
|
|
313
|
-
| any[]
|
|
314
|
-
| {
|
|
315
|
-
traces: any[];
|
|
316
|
-
total: number;
|
|
317
|
-
page: number;
|
|
318
|
-
perPage: number;
|
|
319
|
-
hasMore: boolean;
|
|
320
|
-
}
|
|
321
|
-
> {
|
|
322
|
-
const {
|
|
323
|
-
name,
|
|
324
|
-
scope,
|
|
325
|
-
page,
|
|
326
|
-
perPage: perPageInput,
|
|
327
|
-
attributes,
|
|
328
|
-
filters,
|
|
329
|
-
fromDate,
|
|
330
|
-
toDate,
|
|
331
|
-
returnPaginationResults,
|
|
332
|
-
} = args;
|
|
278
|
+
/**
|
|
279
|
+
* @deprecated use getTracesPaginated instead
|
|
280
|
+
*/
|
|
281
|
+
public async getTraces(args: StorageGetTracesArg): Promise<any[]> {
|
|
282
|
+
if (args.fromDate || args.toDate) {
|
|
283
|
+
(args as any).dateRange = {
|
|
284
|
+
start: args.fromDate,
|
|
285
|
+
end: args.toDate,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const { traces } = await this.getTracesPaginated(args);
|
|
289
|
+
return traces;
|
|
290
|
+
}
|
|
333
291
|
|
|
334
|
-
|
|
292
|
+
public async getTracesPaginated(
|
|
293
|
+
args: {
|
|
294
|
+
name?: string;
|
|
295
|
+
scope?: string;
|
|
296
|
+
attributes?: Record<string, string>;
|
|
297
|
+
filters?: Record<string, any>;
|
|
298
|
+
} & PaginationArgs,
|
|
299
|
+
): Promise<PaginationInfo & { traces: any[] }> {
|
|
300
|
+
const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
|
|
301
|
+
const fromDate = dateRange?.start;
|
|
302
|
+
const toDate = dateRange?.end;
|
|
335
303
|
|
|
336
304
|
try {
|
|
337
305
|
const pattern = `${TABLE_TRACES}:*`;
|
|
338
306
|
const keys = await this.scanKeys(pattern);
|
|
339
307
|
|
|
340
308
|
if (keys.length === 0) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
return [];
|
|
309
|
+
return {
|
|
310
|
+
traces: [],
|
|
311
|
+
total: 0,
|
|
312
|
+
page,
|
|
313
|
+
perPage: perPage || 100,
|
|
314
|
+
hasMore: false,
|
|
315
|
+
};
|
|
351
316
|
}
|
|
352
317
|
|
|
353
318
|
const pipeline = this.redis.pipeline();
|
|
@@ -414,29 +379,23 @@ export class UpstashStore extends MastraStorage {
|
|
|
414
379
|
const end = start + resolvedPerPage;
|
|
415
380
|
const paginatedTraces = transformedTraces.slice(start, end);
|
|
416
381
|
const hasMore = end < total;
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
} else {
|
|
426
|
-
return paginatedTraces;
|
|
427
|
-
}
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
traces: paginatedTraces,
|
|
385
|
+
total,
|
|
386
|
+
page,
|
|
387
|
+
perPage: resolvedPerPage,
|
|
388
|
+
hasMore,
|
|
389
|
+
};
|
|
428
390
|
} catch (error) {
|
|
429
391
|
console.error('Failed to get traces:', error);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
return [];
|
|
392
|
+
return {
|
|
393
|
+
traces: [],
|
|
394
|
+
total: 0,
|
|
395
|
+
page,
|
|
396
|
+
perPage: perPage || 100,
|
|
397
|
+
hasMore: false,
|
|
398
|
+
};
|
|
440
399
|
}
|
|
441
400
|
}
|
|
442
401
|
|
|
@@ -515,44 +474,15 @@ export class UpstashStore extends MastraStorage {
|
|
|
515
474
|
};
|
|
516
475
|
}
|
|
517
476
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
page: number;
|
|
523
|
-
perPage: number;
|
|
524
|
-
hasMore: boolean;
|
|
525
|
-
}>;
|
|
526
|
-
async getThreadsByResourceId(args: { resourceId: string; page?: number; perPage?: number }): Promise<
|
|
527
|
-
| StorageThreadType[]
|
|
528
|
-
| {
|
|
529
|
-
threads: StorageThreadType[];
|
|
530
|
-
total: number;
|
|
531
|
-
page: number;
|
|
532
|
-
perPage: number;
|
|
533
|
-
hasMore: boolean;
|
|
534
|
-
}
|
|
535
|
-
> {
|
|
536
|
-
const resourceId: string = args.resourceId;
|
|
537
|
-
const page: number | undefined = args.page;
|
|
538
|
-
// Determine perPage only if page is actually provided. Otherwise, its value is not critical for the non-paginated path.
|
|
539
|
-
// If page is provided, perPage defaults to 100 if not specified.
|
|
540
|
-
const perPage: number = page !== undefined ? (args.perPage !== undefined ? args.perPage : 100) : 100;
|
|
541
|
-
|
|
477
|
+
/**
|
|
478
|
+
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
479
|
+
*/
|
|
480
|
+
async getThreadsByResourceId({ resourceId }: { resourceId: string }): Promise<StorageThreadType[]> {
|
|
542
481
|
try {
|
|
543
482
|
const pattern = `${TABLE_THREADS}:*`;
|
|
544
483
|
const keys = await this.scanKeys(pattern);
|
|
545
484
|
|
|
546
485
|
if (keys.length === 0) {
|
|
547
|
-
if (page !== undefined) {
|
|
548
|
-
return {
|
|
549
|
-
threads: [],
|
|
550
|
-
total: 0,
|
|
551
|
-
page,
|
|
552
|
-
perPage, // perPage is number here
|
|
553
|
-
hasMore: false,
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
486
|
return [];
|
|
557
487
|
}
|
|
558
488
|
|
|
@@ -574,40 +504,48 @@ export class UpstashStore extends MastraStorage {
|
|
|
574
504
|
}
|
|
575
505
|
|
|
576
506
|
allThreads.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
577
|
-
|
|
578
|
-
if (page !== undefined) {
|
|
579
|
-
// If page is defined, perPage is also a number (due to the defaulting logic above)
|
|
580
|
-
const total = allThreads.length;
|
|
581
|
-
const start = page * perPage;
|
|
582
|
-
const end = start + perPage;
|
|
583
|
-
const paginatedThreads = allThreads.slice(start, end);
|
|
584
|
-
const hasMore = end < total;
|
|
585
|
-
return {
|
|
586
|
-
threads: paginatedThreads,
|
|
587
|
-
total,
|
|
588
|
-
page,
|
|
589
|
-
perPage,
|
|
590
|
-
hasMore,
|
|
591
|
-
};
|
|
592
|
-
} else {
|
|
593
|
-
// page is undefined, return all threads
|
|
594
|
-
return allThreads;
|
|
595
|
-
}
|
|
507
|
+
return allThreads;
|
|
596
508
|
} catch (error) {
|
|
597
509
|
console.error('Error in getThreadsByResourceId:', error);
|
|
598
|
-
if (page !== undefined) {
|
|
599
|
-
return {
|
|
600
|
-
threads: [],
|
|
601
|
-
total: 0,
|
|
602
|
-
page,
|
|
603
|
-
perPage, // perPage is number here
|
|
604
|
-
hasMore: false,
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
510
|
return [];
|
|
608
511
|
}
|
|
609
512
|
}
|
|
610
513
|
|
|
514
|
+
public async getThreadsByResourceIdPaginated(
|
|
515
|
+
args: {
|
|
516
|
+
resourceId: string;
|
|
517
|
+
} & PaginationArgs,
|
|
518
|
+
): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
|
|
519
|
+
const { resourceId, page = 0, perPage = 100 } = args;
|
|
520
|
+
|
|
521
|
+
try {
|
|
522
|
+
const allThreads = await this.getThreadsByResourceId({ resourceId });
|
|
523
|
+
|
|
524
|
+
const total = allThreads.length;
|
|
525
|
+
const start = page * perPage;
|
|
526
|
+
const end = start + perPage;
|
|
527
|
+
const paginatedThreads = allThreads.slice(start, end);
|
|
528
|
+
const hasMore = end < total;
|
|
529
|
+
|
|
530
|
+
return {
|
|
531
|
+
threads: paginatedThreads,
|
|
532
|
+
total,
|
|
533
|
+
page,
|
|
534
|
+
perPage,
|
|
535
|
+
hasMore,
|
|
536
|
+
};
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.error('Error in getThreadsByResourceIdPaginated:', error);
|
|
539
|
+
return {
|
|
540
|
+
threads: [],
|
|
541
|
+
total: 0,
|
|
542
|
+
page,
|
|
543
|
+
perPage,
|
|
544
|
+
hasMore: false,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
611
549
|
async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
|
|
612
550
|
await this.insert({
|
|
613
551
|
tableName: TABLE_THREADS,
|
|
@@ -717,160 +655,13 @@ export class UpstashStore extends MastraStorage {
|
|
|
717
655
|
return list.get.all.v1();
|
|
718
656
|
}
|
|
719
657
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
args: StorageGetMessagesArg & {
|
|
725
|
-
format?: 'v1' | 'v2';
|
|
726
|
-
page: number;
|
|
727
|
-
perPage?: number;
|
|
728
|
-
fromDate?: Date;
|
|
729
|
-
toDate?: Date;
|
|
730
|
-
},
|
|
731
|
-
): Promise<{
|
|
732
|
-
messages: MastraMessageV1[] | MastraMessageV2[];
|
|
733
|
-
total: number;
|
|
734
|
-
page: number;
|
|
735
|
-
perPage: number;
|
|
736
|
-
hasMore: boolean;
|
|
737
|
-
}>;
|
|
738
|
-
public async getMessages({
|
|
739
|
-
threadId,
|
|
740
|
-
selectBy,
|
|
741
|
-
format,
|
|
742
|
-
page,
|
|
743
|
-
perPage = 40,
|
|
744
|
-
fromDate,
|
|
745
|
-
toDate,
|
|
746
|
-
}: StorageGetMessagesArg & {
|
|
747
|
-
format?: 'v1' | 'v2';
|
|
748
|
-
page?: number;
|
|
749
|
-
perPage?: number;
|
|
750
|
-
fromDate?: Date;
|
|
751
|
-
toDate?: Date;
|
|
752
|
-
}): Promise<
|
|
753
|
-
| MastraMessageV1[]
|
|
754
|
-
| MastraMessageV2[]
|
|
755
|
-
| {
|
|
756
|
-
messages: MastraMessageV1[] | MastraMessageV2[];
|
|
757
|
-
total: number;
|
|
758
|
-
page: number;
|
|
759
|
-
perPage: number;
|
|
760
|
-
hasMore: boolean;
|
|
761
|
-
}
|
|
762
|
-
> {
|
|
763
|
-
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
764
|
-
|
|
765
|
-
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
766
|
-
// If pagination is requested, use the new pagination logic
|
|
767
|
-
if (page !== undefined) {
|
|
768
|
-
try {
|
|
769
|
-
// Get all message IDs from the sorted set
|
|
770
|
-
|
|
771
|
-
if (allMessageIds.length === 0) {
|
|
772
|
-
return {
|
|
773
|
-
messages: [],
|
|
774
|
-
total: 0,
|
|
775
|
-
page,
|
|
776
|
-
perPage,
|
|
777
|
-
hasMore: false,
|
|
778
|
-
};
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// Use pipeline to fetch all messages efficiently
|
|
782
|
-
const pipeline = this.redis.pipeline();
|
|
783
|
-
allMessageIds.forEach(id => pipeline.get(this.getMessageKey(threadId, id as string)));
|
|
784
|
-
const results = await pipeline.exec();
|
|
785
|
-
|
|
786
|
-
// Process messages and apply filters - handle undefined results from pipeline
|
|
787
|
-
let messages = results
|
|
788
|
-
.map((result: any) => result as MastraMessageV2 | null)
|
|
789
|
-
.filter((msg): msg is MastraMessageV2 => msg !== null) as (MastraMessageV2 & { _index?: number })[];
|
|
790
|
-
|
|
791
|
-
// Apply date filters if provided
|
|
792
|
-
if (fromDate) {
|
|
793
|
-
messages = messages.filter(msg => msg && new Date(msg.createdAt).getTime() >= fromDate.getTime());
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
if (toDate) {
|
|
797
|
-
messages = messages.filter(msg => msg && new Date(msg.createdAt).getTime() <= toDate.getTime());
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
// Sort messages by their position in the sorted set
|
|
801
|
-
messages.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
|
|
802
|
-
|
|
803
|
-
const total = messages.length;
|
|
804
|
-
|
|
805
|
-
// Apply pagination
|
|
806
|
-
const start = page * perPage;
|
|
807
|
-
const end = start + perPage;
|
|
808
|
-
const hasMore = end < total;
|
|
809
|
-
const paginatedMessages = messages.slice(start, end);
|
|
810
|
-
|
|
811
|
-
// Remove _index before returning and handle format conversion properly
|
|
812
|
-
const prepared = paginatedMessages
|
|
813
|
-
.filter(message => message !== null && message !== undefined)
|
|
814
|
-
.map(message => {
|
|
815
|
-
const { _index, ...messageWithoutIndex } = message as MastraMessageV2 & { _index?: number };
|
|
816
|
-
return messageWithoutIndex as unknown as MastraMessageV1;
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
// Return pagination object with correct format
|
|
820
|
-
if (format === 'v2') {
|
|
821
|
-
// Convert V1 format back to V2 format
|
|
822
|
-
const v2Messages = prepared.map(msg => ({
|
|
823
|
-
...msg,
|
|
824
|
-
content: msg.content || { format: 2, parts: [{ type: 'text', text: '' }] },
|
|
825
|
-
})) as MastraMessageV2[];
|
|
826
|
-
|
|
827
|
-
return {
|
|
828
|
-
messages: v2Messages,
|
|
829
|
-
total,
|
|
830
|
-
page,
|
|
831
|
-
perPage,
|
|
832
|
-
hasMore,
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
return {
|
|
837
|
-
messages: prepared,
|
|
838
|
-
total,
|
|
839
|
-
page,
|
|
840
|
-
perPage,
|
|
841
|
-
hasMore,
|
|
842
|
-
};
|
|
843
|
-
} catch (error) {
|
|
844
|
-
console.error('Failed to get paginated messages:', error);
|
|
845
|
-
return {
|
|
846
|
-
messages: [],
|
|
847
|
-
total: 0,
|
|
848
|
-
page,
|
|
849
|
-
perPage,
|
|
850
|
-
hasMore: false,
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// Original logic for backward compatibility
|
|
856
|
-
// When selectBy is undefined or selectBy.last is undefined, get ALL messages (not just 40)
|
|
857
|
-
let limit: number;
|
|
858
|
-
if (typeof selectBy?.last === 'number') {
|
|
859
|
-
limit = Math.max(0, selectBy.last);
|
|
860
|
-
} else if (selectBy?.last === false) {
|
|
861
|
-
limit = 0;
|
|
862
|
-
} else {
|
|
863
|
-
// No limit specified - get all messages
|
|
864
|
-
limit = Number.MAX_SAFE_INTEGER;
|
|
865
|
-
}
|
|
866
|
-
|
|
658
|
+
private async _getIncludedMessages(
|
|
659
|
+
threadId: string,
|
|
660
|
+
selectBy: StorageGetMessagesArg['selectBy'],
|
|
661
|
+
): Promise<MastraMessageV2[] | MastraMessageV1[]> {
|
|
867
662
|
const messageIds = new Set<string>();
|
|
868
663
|
const messageIdToThreadIds: Record<string, string> = {};
|
|
869
664
|
|
|
870
|
-
if (limit === 0 && !selectBy?.include) {
|
|
871
|
-
return [];
|
|
872
|
-
}
|
|
873
|
-
|
|
874
665
|
// First, get specifically included messages and their context
|
|
875
666
|
if (selectBy?.include?.length) {
|
|
876
667
|
for (const item of selectBy.include) {
|
|
@@ -904,6 +695,47 @@ export class UpstashStore extends MastraStorage {
|
|
|
904
695
|
});
|
|
905
696
|
}
|
|
906
697
|
}
|
|
698
|
+
|
|
699
|
+
const pipeline = this.redis.pipeline();
|
|
700
|
+
Array.from(messageIds).forEach(id => {
|
|
701
|
+
const tId = messageIdToThreadIds[id] || threadId;
|
|
702
|
+
pipeline.get(this.getMessageKey(tId, id as string));
|
|
703
|
+
});
|
|
704
|
+
const results = await pipeline.exec();
|
|
705
|
+
return results.filter(result => result !== null) as MastraMessageV2[] | MastraMessageV1[];
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return [];
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* @deprecated use getMessagesPaginated instead
|
|
713
|
+
*/
|
|
714
|
+
public async getMessages(args: StorageGetMessagesArg & { format?: 'v1' }): Promise<MastraMessageV1[]>;
|
|
715
|
+
public async getMessages(args: StorageGetMessagesArg & { format: 'v2' }): Promise<MastraMessageV2[]>;
|
|
716
|
+
public async getMessages({
|
|
717
|
+
threadId,
|
|
718
|
+
selectBy,
|
|
719
|
+
format,
|
|
720
|
+
}: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
|
|
721
|
+
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
722
|
+
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
723
|
+
// When selectBy is undefined or selectBy.last is undefined, get ALL messages (not just 40)
|
|
724
|
+
let limit: number;
|
|
725
|
+
if (typeof selectBy?.last === 'number') {
|
|
726
|
+
limit = Math.max(0, selectBy.last);
|
|
727
|
+
} else if (selectBy?.last === false) {
|
|
728
|
+
limit = 0;
|
|
729
|
+
} else {
|
|
730
|
+
// No limit specified - get all messages
|
|
731
|
+
limit = Number.MAX_SAFE_INTEGER;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const messageIds = new Set<string>();
|
|
735
|
+
const messageIdToThreadIds: Record<string, string> = {};
|
|
736
|
+
|
|
737
|
+
if (limit === 0 && !selectBy?.include) {
|
|
738
|
+
return [];
|
|
907
739
|
}
|
|
908
740
|
|
|
909
741
|
// Then get the most recent messages (or all if no limit)
|
|
@@ -923,24 +755,36 @@ export class UpstashStore extends MastraStorage {
|
|
|
923
755
|
});
|
|
924
756
|
}
|
|
925
757
|
|
|
758
|
+
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
759
|
+
|
|
926
760
|
// Fetch all needed messages in parallel
|
|
927
|
-
const messages =
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
761
|
+
const messages = [
|
|
762
|
+
...includedMessages,
|
|
763
|
+
...((
|
|
764
|
+
await Promise.all(
|
|
765
|
+
Array.from(messageIds).map(async id => {
|
|
766
|
+
const tId = messageIdToThreadIds[id] || threadId;
|
|
767
|
+
const byThreadId = await this.redis.get<MastraMessageV2 & { _index?: number }>(this.getMessageKey(tId, id));
|
|
768
|
+
if (byThreadId) return byThreadId;
|
|
769
|
+
|
|
770
|
+
return null;
|
|
771
|
+
}),
|
|
772
|
+
)
|
|
773
|
+
).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
|
|
774
|
+
];
|
|
938
775
|
|
|
939
776
|
// Sort messages by their position in the sorted set
|
|
940
777
|
messages.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
|
|
941
778
|
|
|
779
|
+
const seen = new Set<string>();
|
|
780
|
+
const dedupedMessages = messages.filter(row => {
|
|
781
|
+
if (seen.has(row.id)) return false;
|
|
782
|
+
seen.add(row.id);
|
|
783
|
+
return true;
|
|
784
|
+
});
|
|
785
|
+
|
|
942
786
|
// Remove _index before returning and handle format conversion properly
|
|
943
|
-
const prepared =
|
|
787
|
+
const prepared = dedupedMessages
|
|
944
788
|
.filter(message => message !== null && message !== undefined)
|
|
945
789
|
.map(message => {
|
|
946
790
|
const { _index, ...messageWithoutIndex } = message as MastraMessageV2 & { _index?: number };
|
|
@@ -960,6 +804,89 @@ export class UpstashStore extends MastraStorage {
|
|
|
960
804
|
return prepared;
|
|
961
805
|
}
|
|
962
806
|
|
|
807
|
+
public async getMessagesPaginated(
|
|
808
|
+
args: StorageGetMessagesArg & {
|
|
809
|
+
format?: 'v1' | 'v2';
|
|
810
|
+
},
|
|
811
|
+
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
812
|
+
const { threadId, selectBy, format } = args;
|
|
813
|
+
const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
|
|
814
|
+
const fromDate = dateRange?.start;
|
|
815
|
+
const toDate = dateRange?.end;
|
|
816
|
+
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
817
|
+
const messages: (MastraMessageV2 | MastraMessageV1)[] = [];
|
|
818
|
+
|
|
819
|
+
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
820
|
+
messages.push(...includedMessages);
|
|
821
|
+
|
|
822
|
+
try {
|
|
823
|
+
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
824
|
+
if (allMessageIds.length === 0) {
|
|
825
|
+
return {
|
|
826
|
+
messages: [],
|
|
827
|
+
total: 0,
|
|
828
|
+
page,
|
|
829
|
+
perPage,
|
|
830
|
+
hasMore: false,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Use pipeline to fetch all messages efficiently
|
|
835
|
+
const pipeline = this.redis.pipeline();
|
|
836
|
+
allMessageIds.forEach(id => pipeline.get(this.getMessageKey(threadId, id as string)));
|
|
837
|
+
const results = await pipeline.exec();
|
|
838
|
+
|
|
839
|
+
// Process messages and apply filters - handle undefined results from pipeline
|
|
840
|
+
let messagesData = results.filter((msg): msg is MastraMessageV2 | MastraMessageV1 => msg !== null) as (
|
|
841
|
+
| MastraMessageV2
|
|
842
|
+
| MastraMessageV1
|
|
843
|
+
)[];
|
|
844
|
+
|
|
845
|
+
// Apply date filters if provided
|
|
846
|
+
if (fromDate) {
|
|
847
|
+
messagesData = messagesData.filter(msg => msg && new Date(msg.createdAt).getTime() >= fromDate.getTime());
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (toDate) {
|
|
851
|
+
messagesData = messagesData.filter(msg => msg && new Date(msg.createdAt).getTime() <= toDate.getTime());
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Sort messages by their position in the sorted set
|
|
855
|
+
messagesData.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
|
|
856
|
+
|
|
857
|
+
const total = messagesData.length;
|
|
858
|
+
|
|
859
|
+
const start = page * perPage;
|
|
860
|
+
const end = start + perPage;
|
|
861
|
+
const hasMore = end < total;
|
|
862
|
+
const paginatedMessages = messagesData.slice(start, end);
|
|
863
|
+
|
|
864
|
+
messages.push(...paginatedMessages);
|
|
865
|
+
|
|
866
|
+
const list = new MessageList().add(messages, 'memory');
|
|
867
|
+
const finalMessages = (format === `v2` ? list.get.all.v2() : list.get.all.v1()) as
|
|
868
|
+
| MastraMessageV1[]
|
|
869
|
+
| MastraMessageV2[];
|
|
870
|
+
|
|
871
|
+
return {
|
|
872
|
+
messages: finalMessages,
|
|
873
|
+
total,
|
|
874
|
+
page,
|
|
875
|
+
perPage,
|
|
876
|
+
hasMore,
|
|
877
|
+
};
|
|
878
|
+
} catch (error) {
|
|
879
|
+
console.error('Failed to get paginated messages:', error);
|
|
880
|
+
return {
|
|
881
|
+
messages: [],
|
|
882
|
+
total: 0,
|
|
883
|
+
page,
|
|
884
|
+
perPage,
|
|
885
|
+
hasMore: false,
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
963
890
|
async persistWorkflowSnapshot(params: {
|
|
964
891
|
namespace: string;
|
|
965
892
|
workflowName: string;
|
|
@@ -1006,28 +933,17 @@ export class UpstashStore extends MastraStorage {
|
|
|
1006
933
|
* @param options Pagination and filtering options
|
|
1007
934
|
* @returns Object with evals array and total count
|
|
1008
935
|
*/
|
|
1009
|
-
async getEvals(
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
offset?: number;
|
|
1016
|
-
fromDate?: Date;
|
|
1017
|
-
toDate?: Date;
|
|
1018
|
-
}): Promise<{
|
|
1019
|
-
evals: EvalRow[];
|
|
1020
|
-
total: number;
|
|
1021
|
-
page?: number;
|
|
1022
|
-
perPage?: number;
|
|
1023
|
-
hasMore?: boolean;
|
|
1024
|
-
}> {
|
|
936
|
+
async getEvals(
|
|
937
|
+
options?: {
|
|
938
|
+
agentName?: string;
|
|
939
|
+
type?: 'test' | 'live';
|
|
940
|
+
} & PaginationArgs,
|
|
941
|
+
): Promise<PaginationInfo & { evals: EvalRow[] }> {
|
|
1025
942
|
try {
|
|
1026
943
|
// Default pagination parameters
|
|
1027
|
-
const page = options
|
|
1028
|
-
const
|
|
1029
|
-
const
|
|
1030
|
-
const offset = options?.offset;
|
|
944
|
+
const { agentName, type, page = 0, perPage = 100, dateRange } = options || {};
|
|
945
|
+
const fromDate = dateRange?.start;
|
|
946
|
+
const toDate = dateRange?.end;
|
|
1031
947
|
|
|
1032
948
|
// Get all keys that match the evals table pattern using cursor-based scanning
|
|
1033
949
|
const pattern = `${TABLE_EVALS}:*`;
|
|
@@ -1038,8 +954,8 @@ export class UpstashStore extends MastraStorage {
|
|
|
1038
954
|
return {
|
|
1039
955
|
evals: [],
|
|
1040
956
|
total: 0,
|
|
1041
|
-
page
|
|
1042
|
-
perPage
|
|
957
|
+
page,
|
|
958
|
+
perPage,
|
|
1043
959
|
hasMore: false,
|
|
1044
960
|
};
|
|
1045
961
|
}
|
|
@@ -1055,12 +971,12 @@ export class UpstashStore extends MastraStorage {
|
|
|
1055
971
|
.filter((record): record is Record<string, any> => record !== null && typeof record === 'object');
|
|
1056
972
|
|
|
1057
973
|
// Apply agent name filter if provided
|
|
1058
|
-
if (
|
|
1059
|
-
filteredEvals = filteredEvals.filter(record => record.agent_name ===
|
|
974
|
+
if (agentName) {
|
|
975
|
+
filteredEvals = filteredEvals.filter(record => record.agent_name === agentName);
|
|
1060
976
|
}
|
|
1061
977
|
|
|
1062
978
|
// Apply type filter if provided
|
|
1063
|
-
if (
|
|
979
|
+
if (type === 'test') {
|
|
1064
980
|
filteredEvals = filteredEvals.filter(record => {
|
|
1065
981
|
if (!record.test_info) return false;
|
|
1066
982
|
|
|
@@ -1074,7 +990,7 @@ export class UpstashStore extends MastraStorage {
|
|
|
1074
990
|
return false;
|
|
1075
991
|
}
|
|
1076
992
|
});
|
|
1077
|
-
} else if (
|
|
993
|
+
} else if (type === 'live') {
|
|
1078
994
|
filteredEvals = filteredEvals.filter(record => {
|
|
1079
995
|
if (!record.test_info) return true;
|
|
1080
996
|
|
|
@@ -1091,17 +1007,17 @@ export class UpstashStore extends MastraStorage {
|
|
|
1091
1007
|
}
|
|
1092
1008
|
|
|
1093
1009
|
// Apply date filters if provided
|
|
1094
|
-
if (
|
|
1010
|
+
if (fromDate) {
|
|
1095
1011
|
filteredEvals = filteredEvals.filter(record => {
|
|
1096
1012
|
const createdAt = new Date(record.created_at || record.createdAt || 0);
|
|
1097
|
-
return createdAt.getTime() >=
|
|
1013
|
+
return createdAt.getTime() >= fromDate.getTime();
|
|
1098
1014
|
});
|
|
1099
1015
|
}
|
|
1100
1016
|
|
|
1101
|
-
if (
|
|
1017
|
+
if (toDate) {
|
|
1102
1018
|
filteredEvals = filteredEvals.filter(record => {
|
|
1103
1019
|
const createdAt = new Date(record.created_at || record.createdAt || 0);
|
|
1104
|
-
return createdAt.getTime() <=
|
|
1020
|
+
return createdAt.getTime() <= toDate.getTime();
|
|
1105
1021
|
});
|
|
1106
1022
|
}
|
|
1107
1023
|
|
|
@@ -1114,21 +1030,11 @@ export class UpstashStore extends MastraStorage {
|
|
|
1114
1030
|
|
|
1115
1031
|
const total = filteredEvals.length;
|
|
1116
1032
|
|
|
1117
|
-
// Apply pagination
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
// Offset-based pagination
|
|
1123
|
-
paginatedEvals = filteredEvals.slice(offset, offset + limit);
|
|
1124
|
-
hasMore = offset + limit < total;
|
|
1125
|
-
} else {
|
|
1126
|
-
// Page-based pagination
|
|
1127
|
-
const start = page * perPage;
|
|
1128
|
-
const end = start + perPage;
|
|
1129
|
-
paginatedEvals = filteredEvals.slice(start, end);
|
|
1130
|
-
hasMore = end < total;
|
|
1131
|
-
}
|
|
1033
|
+
// Apply pagination
|
|
1034
|
+
const start = page * perPage;
|
|
1035
|
+
const end = start + perPage;
|
|
1036
|
+
const paginatedEvals = filteredEvals.slice(start, end);
|
|
1037
|
+
const hasMore = end < total;
|
|
1132
1038
|
|
|
1133
1039
|
// Transform to EvalRow format
|
|
1134
1040
|
const evals = paginatedEvals.map(record => this.transformEvalRecord(record));
|
|
@@ -1136,17 +1042,18 @@ export class UpstashStore extends MastraStorage {
|
|
|
1136
1042
|
return {
|
|
1137
1043
|
evals,
|
|
1138
1044
|
total,
|
|
1139
|
-
page
|
|
1140
|
-
perPage
|
|
1045
|
+
page,
|
|
1046
|
+
perPage,
|
|
1141
1047
|
hasMore,
|
|
1142
1048
|
};
|
|
1143
1049
|
} catch (error) {
|
|
1050
|
+
const { page = 0, perPage = 100 } = options || {};
|
|
1144
1051
|
console.error('Failed to get evals:', error);
|
|
1145
1052
|
return {
|
|
1146
1053
|
evals: [],
|
|
1147
1054
|
total: 0,
|
|
1148
|
-
page
|
|
1149
|
-
perPage
|
|
1055
|
+
page,
|
|
1056
|
+
perPage,
|
|
1150
1057
|
hasMore: false,
|
|
1151
1058
|
};
|
|
1152
1059
|
}
|