@mastra/upstash 0.11.1-alpha.0 → 0.11.1-alpha.2
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 +26 -0
- package/dist/_tsup-dts-rollup.d.cts +28 -6
- package/dist/_tsup-dts-rollup.d.ts +28 -6
- package/dist/index.cjs +678 -196
- package/dist/index.js +668 -186
- package/package.json +3 -3
- package/src/storage/index.ts +603 -185
- package/src/storage/upstash.test.ts +122 -15
- package/src/vector/filter.test.ts +7 -6
- package/src/vector/filter.ts +10 -4
- package/src/vector/index.test.ts +2 -2
- package/src/vector/index.ts +125 -38
package/src/storage/index.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
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 {
|
|
6
7
|
MastraStorage,
|
|
7
8
|
TABLE_MESSAGES,
|
|
8
9
|
TABLE_THREADS,
|
|
10
|
+
TABLE_RESOURCES,
|
|
9
11
|
TABLE_WORKFLOW_SNAPSHOT,
|
|
10
12
|
TABLE_EVALS,
|
|
11
13
|
TABLE_TRACES,
|
|
@@ -14,6 +16,7 @@ import type {
|
|
|
14
16
|
TABLE_NAMES,
|
|
15
17
|
StorageColumn,
|
|
16
18
|
StorageGetMessagesArg,
|
|
19
|
+
StorageResourceType,
|
|
17
20
|
EvalRow,
|
|
18
21
|
WorkflowRuns,
|
|
19
22
|
WorkflowRun,
|
|
@@ -42,9 +45,11 @@ export class UpstashStore extends MastraStorage {
|
|
|
42
45
|
|
|
43
46
|
public get supports(): {
|
|
44
47
|
selectByIncludeResourceScope: boolean;
|
|
48
|
+
resourceWorkingMemory: boolean;
|
|
45
49
|
} {
|
|
46
50
|
return {
|
|
47
51
|
selectByIncludeResourceScope: true,
|
|
52
|
+
resourceWorkingMemory: true,
|
|
48
53
|
};
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -271,7 +276,17 @@ export class UpstashStore extends MastraStorage {
|
|
|
271
276
|
// Transform to EvalRow format
|
|
272
277
|
return filteredEvals.map(record => this.transformEvalRecord(record));
|
|
273
278
|
} catch (error) {
|
|
274
|
-
|
|
279
|
+
const mastraError = new MastraError(
|
|
280
|
+
{
|
|
281
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_EVALS_BY_AGENT_NAME_FAILED',
|
|
282
|
+
domain: ErrorDomain.STORAGE,
|
|
283
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
284
|
+
details: { agentName },
|
|
285
|
+
},
|
|
286
|
+
error,
|
|
287
|
+
);
|
|
288
|
+
this.logger?.trackException(mastraError);
|
|
289
|
+
this.logger.error(mastraError.toString());
|
|
275
290
|
return [];
|
|
276
291
|
}
|
|
277
292
|
}
|
|
@@ -286,8 +301,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
286
301
|
end: args.toDate,
|
|
287
302
|
};
|
|
288
303
|
}
|
|
289
|
-
|
|
290
|
-
|
|
304
|
+
try {
|
|
305
|
+
const { traces } = await this.getTracesPaginated(args);
|
|
306
|
+
return traces;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
throw new MastraError(
|
|
309
|
+
{
|
|
310
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_TRACES_FAILED',
|
|
311
|
+
domain: ErrorDomain.STORAGE,
|
|
312
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
313
|
+
},
|
|
314
|
+
error,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
291
317
|
}
|
|
292
318
|
|
|
293
319
|
public async getTracesPaginated(
|
|
@@ -389,7 +415,20 @@ export class UpstashStore extends MastraStorage {
|
|
|
389
415
|
hasMore,
|
|
390
416
|
};
|
|
391
417
|
} catch (error) {
|
|
392
|
-
|
|
418
|
+
const mastraError = new MastraError(
|
|
419
|
+
{
|
|
420
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_TRACES_PAGINATED_FAILED',
|
|
421
|
+
domain: ErrorDomain.STORAGE,
|
|
422
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
423
|
+
details: {
|
|
424
|
+
name: args.name || '',
|
|
425
|
+
scope: args.scope || '',
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
error,
|
|
429
|
+
);
|
|
430
|
+
this.logger?.trackException(mastraError);
|
|
431
|
+
this.logger.error(mastraError.toString());
|
|
393
432
|
return {
|
|
394
433
|
traces: [],
|
|
395
434
|
total: 0,
|
|
@@ -409,7 +448,21 @@ export class UpstashStore extends MastraStorage {
|
|
|
409
448
|
}): Promise<void> {
|
|
410
449
|
// Redis is schemaless, so we don't need to create tables
|
|
411
450
|
// But we can store the schema for reference
|
|
412
|
-
|
|
451
|
+
try {
|
|
452
|
+
await this.redis.set(`schema:${tableName}`, schema);
|
|
453
|
+
} catch (error) {
|
|
454
|
+
throw new MastraError(
|
|
455
|
+
{
|
|
456
|
+
id: 'STORAGE_UPSTASH_STORAGE_CREATE_TABLE_FAILED',
|
|
457
|
+
domain: ErrorDomain.STORAGE,
|
|
458
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
459
|
+
details: {
|
|
460
|
+
tableName,
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
error,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
413
466
|
}
|
|
414
467
|
|
|
415
468
|
/**
|
|
@@ -428,13 +481,41 @@ export class UpstashStore extends MastraStorage {
|
|
|
428
481
|
|
|
429
482
|
async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
430
483
|
const pattern = `${tableName}:*`;
|
|
431
|
-
|
|
484
|
+
try {
|
|
485
|
+
await this.scanAndDelete(pattern);
|
|
486
|
+
} catch (error) {
|
|
487
|
+
throw new MastraError(
|
|
488
|
+
{
|
|
489
|
+
id: 'STORAGE_UPSTASH_STORAGE_CLEAR_TABLE_FAILED',
|
|
490
|
+
domain: ErrorDomain.STORAGE,
|
|
491
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
492
|
+
details: {
|
|
493
|
+
tableName,
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
error,
|
|
497
|
+
);
|
|
498
|
+
}
|
|
432
499
|
}
|
|
433
500
|
|
|
434
501
|
async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
|
|
435
502
|
const { key, processedRecord } = this.processRecord(tableName, record);
|
|
436
503
|
|
|
437
|
-
|
|
504
|
+
try {
|
|
505
|
+
await this.redis.set(key, processedRecord);
|
|
506
|
+
} catch (error) {
|
|
507
|
+
throw new MastraError(
|
|
508
|
+
{
|
|
509
|
+
id: 'STORAGE_UPSTASH_STORAGE_INSERT_FAILED',
|
|
510
|
+
domain: ErrorDomain.STORAGE,
|
|
511
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
512
|
+
details: {
|
|
513
|
+
tableName,
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
error,
|
|
517
|
+
);
|
|
518
|
+
}
|
|
438
519
|
}
|
|
439
520
|
|
|
440
521
|
async batchInsert(input: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
@@ -442,37 +523,79 @@ export class UpstashStore extends MastraStorage {
|
|
|
442
523
|
if (!records.length) return;
|
|
443
524
|
|
|
444
525
|
const batchSize = 1000;
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
|
|
526
|
+
try {
|
|
527
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
528
|
+
const batch = records.slice(i, i + batchSize);
|
|
529
|
+
const pipeline = this.redis.pipeline();
|
|
530
|
+
for (const record of batch) {
|
|
531
|
+
const { key, processedRecord } = this.processRecord(tableName, record);
|
|
532
|
+
pipeline.set(key, processedRecord);
|
|
533
|
+
}
|
|
534
|
+
await pipeline.exec();
|
|
451
535
|
}
|
|
452
|
-
|
|
536
|
+
} catch (error) {
|
|
537
|
+
throw new MastraError(
|
|
538
|
+
{
|
|
539
|
+
id: 'STORAGE_UPSTASH_STORAGE_BATCH_INSERT_FAILED',
|
|
540
|
+
domain: ErrorDomain.STORAGE,
|
|
541
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
542
|
+
details: {
|
|
543
|
+
tableName,
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
error,
|
|
547
|
+
);
|
|
453
548
|
}
|
|
454
549
|
}
|
|
455
550
|
|
|
456
551
|
async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
|
|
457
552
|
const key = this.getKey(tableName, keys);
|
|
458
|
-
|
|
459
|
-
|
|
553
|
+
try {
|
|
554
|
+
const data = await this.redis.get<R>(key);
|
|
555
|
+
return data || null;
|
|
556
|
+
} catch (error) {
|
|
557
|
+
throw new MastraError(
|
|
558
|
+
{
|
|
559
|
+
id: 'STORAGE_UPSTASH_STORAGE_LOAD_FAILED',
|
|
560
|
+
domain: ErrorDomain.STORAGE,
|
|
561
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
562
|
+
details: {
|
|
563
|
+
tableName,
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
error,
|
|
567
|
+
);
|
|
568
|
+
}
|
|
460
569
|
}
|
|
461
570
|
|
|
462
571
|
async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
572
|
+
try {
|
|
573
|
+
const thread = await this.load<StorageThreadType>({
|
|
574
|
+
tableName: TABLE_THREADS,
|
|
575
|
+
keys: { id: threadId },
|
|
576
|
+
});
|
|
467
577
|
|
|
468
|
-
|
|
578
|
+
if (!thread) return null;
|
|
469
579
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
580
|
+
return {
|
|
581
|
+
...thread,
|
|
582
|
+
createdAt: this.ensureDate(thread.createdAt)!,
|
|
583
|
+
updatedAt: this.ensureDate(thread.updatedAt)!,
|
|
584
|
+
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
585
|
+
};
|
|
586
|
+
} catch (error) {
|
|
587
|
+
throw new MastraError(
|
|
588
|
+
{
|
|
589
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_THREAD_BY_ID_FAILED',
|
|
590
|
+
domain: ErrorDomain.STORAGE,
|
|
591
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
592
|
+
details: {
|
|
593
|
+
threadId,
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
error,
|
|
597
|
+
);
|
|
598
|
+
}
|
|
476
599
|
}
|
|
477
600
|
|
|
478
601
|
/**
|
|
@@ -507,7 +630,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
507
630
|
allThreads.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
508
631
|
return allThreads;
|
|
509
632
|
} catch (error) {
|
|
510
|
-
|
|
633
|
+
const mastraError = new MastraError(
|
|
634
|
+
{
|
|
635
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED',
|
|
636
|
+
domain: ErrorDomain.STORAGE,
|
|
637
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
638
|
+
details: {
|
|
639
|
+
resourceId,
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
error,
|
|
643
|
+
);
|
|
644
|
+
this.logger?.trackException(mastraError);
|
|
645
|
+
this.logger.error(mastraError.toString());
|
|
511
646
|
return [];
|
|
512
647
|
}
|
|
513
648
|
}
|
|
@@ -536,7 +671,21 @@ export class UpstashStore extends MastraStorage {
|
|
|
536
671
|
hasMore,
|
|
537
672
|
};
|
|
538
673
|
} catch (error) {
|
|
539
|
-
|
|
674
|
+
const mastraError = new MastraError(
|
|
675
|
+
{
|
|
676
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED',
|
|
677
|
+
domain: ErrorDomain.STORAGE,
|
|
678
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
679
|
+
details: {
|
|
680
|
+
resourceId,
|
|
681
|
+
page,
|
|
682
|
+
perPage,
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
error,
|
|
686
|
+
);
|
|
687
|
+
this.logger?.trackException(mastraError);
|
|
688
|
+
this.logger.error(mastraError.toString());
|
|
540
689
|
return {
|
|
541
690
|
threads: [],
|
|
542
691
|
total: 0,
|
|
@@ -548,11 +697,28 @@ export class UpstashStore extends MastraStorage {
|
|
|
548
697
|
}
|
|
549
698
|
|
|
550
699
|
async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
700
|
+
try {
|
|
701
|
+
await this.insert({
|
|
702
|
+
tableName: TABLE_THREADS,
|
|
703
|
+
record: thread,
|
|
704
|
+
});
|
|
705
|
+
return thread;
|
|
706
|
+
} catch (error) {
|
|
707
|
+
const mastraError = new MastraError(
|
|
708
|
+
{
|
|
709
|
+
id: 'STORAGE_UPSTASH_STORAGE_SAVE_THREAD_FAILED',
|
|
710
|
+
domain: ErrorDomain.STORAGE,
|
|
711
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
712
|
+
details: {
|
|
713
|
+
threadId: thread.id,
|
|
714
|
+
},
|
|
715
|
+
},
|
|
716
|
+
error,
|
|
717
|
+
);
|
|
718
|
+
this.logger?.trackException(mastraError);
|
|
719
|
+
this.logger.error(mastraError.toString());
|
|
720
|
+
throw mastraError;
|
|
721
|
+
}
|
|
556
722
|
}
|
|
557
723
|
|
|
558
724
|
async updateThread({
|
|
@@ -566,7 +732,15 @@ export class UpstashStore extends MastraStorage {
|
|
|
566
732
|
}): Promise<StorageThreadType> {
|
|
567
733
|
const thread = await this.getThreadById({ threadId: id });
|
|
568
734
|
if (!thread) {
|
|
569
|
-
throw new
|
|
735
|
+
throw new MastraError({
|
|
736
|
+
id: 'STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED',
|
|
737
|
+
domain: ErrorDomain.STORAGE,
|
|
738
|
+
category: ErrorCategory.USER,
|
|
739
|
+
text: `Thread ${id} not found`,
|
|
740
|
+
details: {
|
|
741
|
+
threadId: id,
|
|
742
|
+
},
|
|
743
|
+
});
|
|
570
744
|
}
|
|
571
745
|
|
|
572
746
|
const updatedThread = {
|
|
@@ -578,30 +752,58 @@ export class UpstashStore extends MastraStorage {
|
|
|
578
752
|
},
|
|
579
753
|
};
|
|
580
754
|
|
|
581
|
-
|
|
582
|
-
|
|
755
|
+
try {
|
|
756
|
+
await this.saveThread({ thread: updatedThread });
|
|
757
|
+
return updatedThread;
|
|
758
|
+
} catch (error) {
|
|
759
|
+
throw new MastraError(
|
|
760
|
+
{
|
|
761
|
+
id: 'STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED',
|
|
762
|
+
domain: ErrorDomain.STORAGE,
|
|
763
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
764
|
+
details: {
|
|
765
|
+
threadId: id,
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
error,
|
|
769
|
+
);
|
|
770
|
+
}
|
|
583
771
|
}
|
|
584
772
|
|
|
585
773
|
async deleteThread({ threadId }: { threadId: string }): Promise<void> {
|
|
586
774
|
// Delete thread metadata and sorted set
|
|
587
775
|
const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
|
|
588
776
|
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
589
|
-
|
|
777
|
+
try {
|
|
778
|
+
const messageIds: string[] = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
590
779
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
780
|
+
const pipeline = this.redis.pipeline();
|
|
781
|
+
pipeline.del(threadKey);
|
|
782
|
+
pipeline.del(threadMessagesKey);
|
|
594
783
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
784
|
+
for (let i = 0; i < messageIds.length; i++) {
|
|
785
|
+
const messageId = messageIds[i];
|
|
786
|
+
const messageKey = this.getMessageKey(threadId, messageId as string);
|
|
787
|
+
pipeline.del(messageKey);
|
|
788
|
+
}
|
|
600
789
|
|
|
601
|
-
|
|
790
|
+
await pipeline.exec();
|
|
602
791
|
|
|
603
|
-
|
|
604
|
-
|
|
792
|
+
// Bulk delete all message keys for this thread if any remain
|
|
793
|
+
await this.scanAndDelete(this.getMessageKey(threadId, '*'));
|
|
794
|
+
} catch (error) {
|
|
795
|
+
throw new MastraError(
|
|
796
|
+
{
|
|
797
|
+
id: 'STORAGE_UPSTASH_STORAGE_DELETE_THREAD_FAILED',
|
|
798
|
+
domain: ErrorDomain.STORAGE,
|
|
799
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
800
|
+
details: {
|
|
801
|
+
threadId,
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
error,
|
|
805
|
+
);
|
|
806
|
+
}
|
|
605
807
|
}
|
|
606
808
|
|
|
607
809
|
async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
|
|
@@ -613,14 +815,25 @@ export class UpstashStore extends MastraStorage {
|
|
|
613
815
|
if (messages.length === 0) return [];
|
|
614
816
|
|
|
615
817
|
const threadId = messages[0]?.threadId;
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
818
|
+
try {
|
|
819
|
+
if (!threadId) {
|
|
820
|
+
throw new Error('Thread ID is required');
|
|
821
|
+
}
|
|
619
822
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
823
|
+
// Check if thread exists
|
|
824
|
+
const thread = await this.getThreadById({ threadId });
|
|
825
|
+
if (!thread) {
|
|
826
|
+
throw new Error(`Thread ${threadId} not found`);
|
|
827
|
+
}
|
|
828
|
+
} catch (error) {
|
|
829
|
+
throw new MastraError(
|
|
830
|
+
{
|
|
831
|
+
id: 'STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_INVALID_ARGS',
|
|
832
|
+
domain: ErrorDomain.STORAGE,
|
|
833
|
+
category: ErrorCategory.USER,
|
|
834
|
+
},
|
|
835
|
+
error,
|
|
836
|
+
);
|
|
624
837
|
}
|
|
625
838
|
|
|
626
839
|
// Add an index to each message to maintain order
|
|
@@ -633,41 +846,76 @@ export class UpstashStore extends MastraStorage {
|
|
|
633
846
|
const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
|
|
634
847
|
const existingThread = await this.redis.get<StorageThreadType>(threadKey);
|
|
635
848
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
849
|
+
try {
|
|
850
|
+
const batchSize = 1000;
|
|
851
|
+
for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
|
|
852
|
+
const batch = messagesWithIndex.slice(i, i + batchSize);
|
|
853
|
+
const pipeline = this.redis.pipeline();
|
|
854
|
+
|
|
855
|
+
for (const message of batch) {
|
|
856
|
+
const key = this.getMessageKey(message.threadId!, message.id);
|
|
857
|
+
const createdAtScore = new Date(message.createdAt).getTime();
|
|
858
|
+
const score = message._index !== undefined ? message._index : createdAtScore;
|
|
859
|
+
|
|
860
|
+
// Check if this message id exists in another thread
|
|
861
|
+
const existingKeyPattern = this.getMessageKey('*', message.id);
|
|
862
|
+
const keys = await this.scanKeys(existingKeyPattern);
|
|
863
|
+
|
|
864
|
+
if (keys.length > 0) {
|
|
865
|
+
const pipeline2 = this.redis.pipeline();
|
|
866
|
+
keys.forEach(key => pipeline2.get(key));
|
|
867
|
+
const results = await pipeline2.exec();
|
|
868
|
+
const existingMessages = results.filter(
|
|
869
|
+
(msg): msg is MastraMessageV2 | MastraMessageV1 => msg !== null,
|
|
870
|
+
) as (MastraMessageV2 | MastraMessageV1)[];
|
|
871
|
+
for (const existingMessage of existingMessages) {
|
|
872
|
+
const existingMessageKey = this.getMessageKey(existingMessage.threadId!, existingMessage.id);
|
|
873
|
+
if (existingMessage && existingMessage.threadId !== message.threadId) {
|
|
874
|
+
pipeline.del(existingMessageKey);
|
|
875
|
+
// Remove from old thread's sorted set
|
|
876
|
+
pipeline.zrem(this.getThreadMessagesKey(existingMessage.threadId!), existingMessage.id);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
640
880
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
const createdAtScore = new Date(message.createdAt).getTime();
|
|
644
|
-
const score = message._index !== undefined ? message._index : createdAtScore;
|
|
881
|
+
// Store the message data
|
|
882
|
+
pipeline.set(key, message);
|
|
645
883
|
|
|
646
|
-
|
|
647
|
-
|
|
884
|
+
// Add to sorted set for this thread
|
|
885
|
+
pipeline.zadd(this.getThreadMessagesKey(message.threadId!), {
|
|
886
|
+
score,
|
|
887
|
+
member: message.id,
|
|
888
|
+
});
|
|
889
|
+
}
|
|
648
890
|
|
|
649
|
-
//
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
891
|
+
// Update the thread's updatedAt field (only in the first batch)
|
|
892
|
+
if (i === 0 && existingThread) {
|
|
893
|
+
const updatedThread = {
|
|
894
|
+
...existingThread,
|
|
895
|
+
updatedAt: new Date(),
|
|
896
|
+
};
|
|
897
|
+
pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
|
|
898
|
+
}
|
|
655
899
|
|
|
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);
|
|
900
|
+
await pipeline.exec();
|
|
663
901
|
}
|
|
664
902
|
|
|
665
|
-
|
|
903
|
+
const list = new MessageList().add(messages, 'memory');
|
|
904
|
+
if (format === `v2`) return list.get.all.v2();
|
|
905
|
+
return list.get.all.v1();
|
|
906
|
+
} catch (error) {
|
|
907
|
+
throw new MastraError(
|
|
908
|
+
{
|
|
909
|
+
id: 'STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_FAILED',
|
|
910
|
+
domain: ErrorDomain.STORAGE,
|
|
911
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
912
|
+
details: {
|
|
913
|
+
threadId,
|
|
914
|
+
},
|
|
915
|
+
},
|
|
916
|
+
error,
|
|
917
|
+
);
|
|
666
918
|
}
|
|
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
919
|
}
|
|
672
920
|
|
|
673
921
|
private async _getIncludedMessages(
|
|
@@ -734,89 +982,96 @@ export class UpstashStore extends MastraStorage {
|
|
|
734
982
|
format,
|
|
735
983
|
}: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
|
|
736
984
|
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
|
-
}
|
|
985
|
+
try {
|
|
986
|
+
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
987
|
+
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
748
988
|
|
|
749
|
-
|
|
750
|
-
|
|
989
|
+
const messageIds = new Set<string>();
|
|
990
|
+
const messageIdToThreadIds: Record<string, string> = {};
|
|
751
991
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
// Then get the most recent messages (or all if no limit)
|
|
757
|
-
if (limit === Number.MAX_SAFE_INTEGER) {
|
|
758
|
-
// Get all messages
|
|
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
|
-
}
|
|
992
|
+
if (limit === 0 && !selectBy?.include) {
|
|
993
|
+
return [];
|
|
994
|
+
}
|
|
772
995
|
|
|
773
|
-
|
|
996
|
+
// Then get the most recent messages (or all if no limit)
|
|
997
|
+
if (limit === Number.MAX_SAFE_INTEGER) {
|
|
998
|
+
// Get all messages
|
|
999
|
+
const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
1000
|
+
allIds.forEach(id => {
|
|
1001
|
+
messageIds.add(id as string);
|
|
1002
|
+
messageIdToThreadIds[id as string] = threadId;
|
|
1003
|
+
});
|
|
1004
|
+
} else if (limit > 0) {
|
|
1005
|
+
// Get limited number of recent messages
|
|
1006
|
+
const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
|
|
1007
|
+
latestIds.forEach(id => {
|
|
1008
|
+
messageIds.add(id as string);
|
|
1009
|
+
messageIdToThreadIds[id as string] = threadId;
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
774
1012
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1013
|
+
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1014
|
+
|
|
1015
|
+
// Fetch all needed messages in parallel
|
|
1016
|
+
const messages = [
|
|
1017
|
+
...includedMessages,
|
|
1018
|
+
...((
|
|
1019
|
+
await Promise.all(
|
|
1020
|
+
Array.from(messageIds).map(async id => {
|
|
1021
|
+
const tId = messageIdToThreadIds[id] || threadId;
|
|
1022
|
+
const byThreadId = await this.redis.get<MastraMessageV2 & { _index?: number }>(
|
|
1023
|
+
this.getMessageKey(tId, id),
|
|
1024
|
+
);
|
|
1025
|
+
if (byThreadId) return byThreadId;
|
|
1026
|
+
|
|
1027
|
+
return null;
|
|
1028
|
+
}),
|
|
1029
|
+
)
|
|
1030
|
+
).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
|
|
1031
|
+
];
|
|
784
1032
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
)
|
|
788
|
-
).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
|
|
789
|
-
];
|
|
1033
|
+
// Sort messages by their position in the sorted set
|
|
1034
|
+
messages.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
|
|
790
1035
|
|
|
791
|
-
|
|
792
|
-
|
|
1036
|
+
const seen = new Set<string>();
|
|
1037
|
+
const dedupedMessages = messages.filter(row => {
|
|
1038
|
+
if (seen.has(row.id)) return false;
|
|
1039
|
+
seen.add(row.id);
|
|
1040
|
+
return true;
|
|
1041
|
+
});
|
|
793
1042
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1043
|
+
// Remove _index before returning and handle format conversion properly
|
|
1044
|
+
const prepared = dedupedMessages
|
|
1045
|
+
.filter(message => message !== null && message !== undefined)
|
|
1046
|
+
.map(message => {
|
|
1047
|
+
const { _index, ...messageWithoutIndex } = message as MastraMessageV2 & { _index?: number };
|
|
1048
|
+
return messageWithoutIndex as unknown as MastraMessageV1;
|
|
1049
|
+
});
|
|
800
1050
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1051
|
+
// For backward compatibility, return messages directly without using MessageList
|
|
1052
|
+
// since MessageList has deduplication logic that can cause issues
|
|
1053
|
+
if (format === 'v2') {
|
|
1054
|
+
// Convert V1 format back to V2 format
|
|
1055
|
+
return prepared.map(msg => ({
|
|
1056
|
+
...msg,
|
|
1057
|
+
content: msg.content || { format: 2, parts: [{ type: 'text', text: '' }] },
|
|
1058
|
+
})) as MastraMessageV2[];
|
|
1059
|
+
}
|
|
808
1060
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1061
|
+
return prepared;
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
throw new MastraError(
|
|
1064
|
+
{
|
|
1065
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_MESSAGES_FAILED',
|
|
1066
|
+
domain: ErrorDomain.STORAGE,
|
|
1067
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1068
|
+
details: {
|
|
1069
|
+
threadId,
|
|
1070
|
+
},
|
|
1071
|
+
},
|
|
1072
|
+
error,
|
|
1073
|
+
);
|
|
817
1074
|
}
|
|
818
|
-
|
|
819
|
-
return prepared;
|
|
820
1075
|
}
|
|
821
1076
|
|
|
822
1077
|
public async getMessagesPaginated(
|
|
@@ -831,10 +1086,10 @@ export class UpstashStore extends MastraStorage {
|
|
|
831
1086
|
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
832
1087
|
const messages: (MastraMessageV2 | MastraMessageV1)[] = [];
|
|
833
1088
|
|
|
834
|
-
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
835
|
-
messages.push(...includedMessages);
|
|
836
|
-
|
|
837
1089
|
try {
|
|
1090
|
+
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1091
|
+
messages.push(...includedMessages);
|
|
1092
|
+
|
|
838
1093
|
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
839
1094
|
if (allMessageIds.length === 0) {
|
|
840
1095
|
return {
|
|
@@ -891,7 +1146,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
891
1146
|
hasMore,
|
|
892
1147
|
};
|
|
893
1148
|
} catch (error) {
|
|
894
|
-
|
|
1149
|
+
const mastraError = new MastraError(
|
|
1150
|
+
{
|
|
1151
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_MESSAGES_PAGINATED_FAILED',
|
|
1152
|
+
domain: ErrorDomain.STORAGE,
|
|
1153
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1154
|
+
details: {
|
|
1155
|
+
threadId,
|
|
1156
|
+
},
|
|
1157
|
+
},
|
|
1158
|
+
error,
|
|
1159
|
+
);
|
|
1160
|
+
this.logger.error(mastraError.toString());
|
|
1161
|
+
this.logger?.trackException(mastraError);
|
|
895
1162
|
return {
|
|
896
1163
|
messages: [],
|
|
897
1164
|
total: 0,
|
|
@@ -909,17 +1176,33 @@ export class UpstashStore extends MastraStorage {
|
|
|
909
1176
|
snapshot: WorkflowRunState;
|
|
910
1177
|
}): Promise<void> {
|
|
911
1178
|
const { namespace = 'workflows', workflowName, runId, snapshot } = params;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1179
|
+
try {
|
|
1180
|
+
await this.insert({
|
|
1181
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1182
|
+
record: {
|
|
1183
|
+
namespace,
|
|
1184
|
+
workflow_name: workflowName,
|
|
1185
|
+
run_id: runId,
|
|
1186
|
+
snapshot,
|
|
1187
|
+
createdAt: new Date(),
|
|
1188
|
+
updatedAt: new Date(),
|
|
1189
|
+
},
|
|
1190
|
+
});
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
throw new MastraError(
|
|
1193
|
+
{
|
|
1194
|
+
id: 'STORAGE_UPSTASH_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED',
|
|
1195
|
+
domain: ErrorDomain.STORAGE,
|
|
1196
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1197
|
+
details: {
|
|
1198
|
+
namespace,
|
|
1199
|
+
workflowName,
|
|
1200
|
+
runId,
|
|
1201
|
+
},
|
|
1202
|
+
},
|
|
1203
|
+
error,
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
923
1206
|
}
|
|
924
1207
|
|
|
925
1208
|
async loadWorkflowSnapshot(params: {
|
|
@@ -933,14 +1216,30 @@ export class UpstashStore extends MastraStorage {
|
|
|
933
1216
|
workflow_name: workflowName,
|
|
934
1217
|
run_id: runId,
|
|
935
1218
|
});
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1219
|
+
try {
|
|
1220
|
+
const data = await this.redis.get<{
|
|
1221
|
+
namespace: string;
|
|
1222
|
+
workflow_name: string;
|
|
1223
|
+
run_id: string;
|
|
1224
|
+
snapshot: WorkflowRunState;
|
|
1225
|
+
}>(key);
|
|
1226
|
+
if (!data) return null;
|
|
1227
|
+
return data.snapshot;
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
throw new MastraError(
|
|
1230
|
+
{
|
|
1231
|
+
id: 'STORAGE_UPSTASH_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED',
|
|
1232
|
+
domain: ErrorDomain.STORAGE,
|
|
1233
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1234
|
+
details: {
|
|
1235
|
+
namespace,
|
|
1236
|
+
workflowName,
|
|
1237
|
+
runId,
|
|
1238
|
+
},
|
|
1239
|
+
},
|
|
1240
|
+
error,
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
944
1243
|
}
|
|
945
1244
|
|
|
946
1245
|
/**
|
|
@@ -1063,7 +1362,20 @@ export class UpstashStore extends MastraStorage {
|
|
|
1063
1362
|
};
|
|
1064
1363
|
} catch (error) {
|
|
1065
1364
|
const { page = 0, perPage = 100 } = options || {};
|
|
1066
|
-
|
|
1365
|
+
const mastraError = new MastraError(
|
|
1366
|
+
{
|
|
1367
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_EVALS_FAILED',
|
|
1368
|
+
domain: ErrorDomain.STORAGE,
|
|
1369
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1370
|
+
details: {
|
|
1371
|
+
page,
|
|
1372
|
+
perPage,
|
|
1373
|
+
},
|
|
1374
|
+
},
|
|
1375
|
+
error,
|
|
1376
|
+
);
|
|
1377
|
+
this.logger.error(mastraError.toString());
|
|
1378
|
+
this.logger?.trackException(mastraError);
|
|
1067
1379
|
return {
|
|
1068
1380
|
evals: [],
|
|
1069
1381
|
total: 0,
|
|
@@ -1146,8 +1458,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
1146
1458
|
|
|
1147
1459
|
return { runs, total };
|
|
1148
1460
|
} catch (error) {
|
|
1149
|
-
|
|
1150
|
-
|
|
1461
|
+
throw new MastraError(
|
|
1462
|
+
{
|
|
1463
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUNS_FAILED',
|
|
1464
|
+
domain: ErrorDomain.STORAGE,
|
|
1465
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1466
|
+
details: {
|
|
1467
|
+
namespace,
|
|
1468
|
+
workflowName: workflowName || '',
|
|
1469
|
+
resourceId: resourceId || '',
|
|
1470
|
+
},
|
|
1471
|
+
},
|
|
1472
|
+
error,
|
|
1473
|
+
);
|
|
1151
1474
|
}
|
|
1152
1475
|
}
|
|
1153
1476
|
|
|
@@ -1180,8 +1503,19 @@ export class UpstashStore extends MastraStorage {
|
|
|
1180
1503
|
if (!data) return null;
|
|
1181
1504
|
return this.parseWorkflowRun(data);
|
|
1182
1505
|
} catch (error) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1506
|
+
throw new MastraError(
|
|
1507
|
+
{
|
|
1508
|
+
id: 'STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED',
|
|
1509
|
+
domain: ErrorDomain.STORAGE,
|
|
1510
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1511
|
+
details: {
|
|
1512
|
+
namespace,
|
|
1513
|
+
runId,
|
|
1514
|
+
workflowName: workflowName || '',
|
|
1515
|
+
},
|
|
1516
|
+
},
|
|
1517
|
+
error,
|
|
1518
|
+
);
|
|
1185
1519
|
}
|
|
1186
1520
|
}
|
|
1187
1521
|
|
|
@@ -1199,4 +1533,88 @@ export class UpstashStore extends MastraStorage {
|
|
|
1199
1533
|
this.logger.error('updateMessages is not yet implemented in UpstashStore');
|
|
1200
1534
|
throw new Error('Method not implemented');
|
|
1201
1535
|
}
|
|
1536
|
+
|
|
1537
|
+
async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
|
|
1538
|
+
try {
|
|
1539
|
+
const key = `${TABLE_RESOURCES}:${resourceId}`;
|
|
1540
|
+
const data = await this.redis.get<StorageResourceType>(key);
|
|
1541
|
+
|
|
1542
|
+
if (!data) {
|
|
1543
|
+
return null;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
return {
|
|
1547
|
+
...data,
|
|
1548
|
+
createdAt: new Date(data.createdAt),
|
|
1549
|
+
updatedAt: new Date(data.updatedAt),
|
|
1550
|
+
// Ensure workingMemory is always returned as a string, regardless of automatic parsing
|
|
1551
|
+
workingMemory: typeof data.workingMemory === 'object' ? JSON.stringify(data.workingMemory) : data.workingMemory,
|
|
1552
|
+
metadata: typeof data.metadata === 'string' ? JSON.parse(data.metadata) : data.metadata,
|
|
1553
|
+
};
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
this.logger.error('Error getting resource by ID:', error);
|
|
1556
|
+
throw error;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
|
|
1561
|
+
try {
|
|
1562
|
+
const key = `${TABLE_RESOURCES}:${resource.id}`;
|
|
1563
|
+
const serializedResource = {
|
|
1564
|
+
...resource,
|
|
1565
|
+
metadata: JSON.stringify(resource.metadata),
|
|
1566
|
+
createdAt: resource.createdAt.toISOString(),
|
|
1567
|
+
updatedAt: resource.updatedAt.toISOString(),
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
await this.redis.set(key, serializedResource);
|
|
1571
|
+
|
|
1572
|
+
return resource;
|
|
1573
|
+
} catch (error) {
|
|
1574
|
+
this.logger.error('Error saving resource:', error);
|
|
1575
|
+
throw error;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
async updateResource({
|
|
1580
|
+
resourceId,
|
|
1581
|
+
workingMemory,
|
|
1582
|
+
metadata,
|
|
1583
|
+
}: {
|
|
1584
|
+
resourceId: string;
|
|
1585
|
+
workingMemory?: string;
|
|
1586
|
+
metadata?: Record<string, unknown>;
|
|
1587
|
+
}): Promise<StorageResourceType> {
|
|
1588
|
+
try {
|
|
1589
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1590
|
+
|
|
1591
|
+
if (!existingResource) {
|
|
1592
|
+
// Create new resource if it doesn't exist
|
|
1593
|
+
const newResource: StorageResourceType = {
|
|
1594
|
+
id: resourceId,
|
|
1595
|
+
workingMemory,
|
|
1596
|
+
metadata: metadata || {},
|
|
1597
|
+
createdAt: new Date(),
|
|
1598
|
+
updatedAt: new Date(),
|
|
1599
|
+
};
|
|
1600
|
+
return this.saveResource({ resource: newResource });
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
const updatedResource = {
|
|
1604
|
+
...existingResource,
|
|
1605
|
+
workingMemory: workingMemory !== undefined ? workingMemory : existingResource.workingMemory,
|
|
1606
|
+
metadata: {
|
|
1607
|
+
...existingResource.metadata,
|
|
1608
|
+
...metadata,
|
|
1609
|
+
},
|
|
1610
|
+
updatedAt: new Date(),
|
|
1611
|
+
};
|
|
1612
|
+
|
|
1613
|
+
await this.saveResource({ resource: updatedResource });
|
|
1614
|
+
return updatedResource;
|
|
1615
|
+
} catch (error) {
|
|
1616
|
+
this.logger.error('Error updating resource:', error);
|
|
1617
|
+
throw error;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1202
1620
|
}
|