@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
|
@@ -321,9 +321,9 @@ describe('UpstashStore', () => {
|
|
|
321
321
|
|
|
322
322
|
it('should save and retrieve messages in order', async () => {
|
|
323
323
|
const messages: MastraMessageV2[] = [
|
|
324
|
-
createSampleMessageV2({ threadId, content: 'First' }),
|
|
325
|
-
createSampleMessageV2({ threadId, content: 'Second' }),
|
|
326
|
-
createSampleMessageV2({ threadId, content: 'Third' }),
|
|
324
|
+
createSampleMessageV2({ threadId, content: { content: 'First' } }),
|
|
325
|
+
createSampleMessageV2({ threadId, content: { content: 'Second' } }),
|
|
326
|
+
createSampleMessageV2({ threadId, content: { content: 'Third' } }),
|
|
327
327
|
];
|
|
328
328
|
|
|
329
329
|
await store.saveMessages({ messages, format: 'v2' });
|
|
@@ -344,16 +344,48 @@ describe('UpstashStore', () => {
|
|
|
344
344
|
await store.saveThread({ thread: thread3 });
|
|
345
345
|
|
|
346
346
|
const messages: MastraMessageV2[] = [
|
|
347
|
-
createSampleMessageV2({
|
|
348
|
-
|
|
349
|
-
|
|
347
|
+
createSampleMessageV2({
|
|
348
|
+
threadId: 'thread-one',
|
|
349
|
+
content: { content: 'First' },
|
|
350
|
+
resourceId: 'cross-thread-resource',
|
|
351
|
+
}),
|
|
352
|
+
createSampleMessageV2({
|
|
353
|
+
threadId: 'thread-one',
|
|
354
|
+
content: { content: 'Second' },
|
|
355
|
+
resourceId: 'cross-thread-resource',
|
|
356
|
+
}),
|
|
357
|
+
createSampleMessageV2({
|
|
358
|
+
threadId: 'thread-one',
|
|
359
|
+
content: { content: 'Third' },
|
|
360
|
+
resourceId: 'cross-thread-resource',
|
|
361
|
+
}),
|
|
350
362
|
|
|
351
|
-
createSampleMessageV2({
|
|
352
|
-
|
|
353
|
-
|
|
363
|
+
createSampleMessageV2({
|
|
364
|
+
threadId: 'thread-two',
|
|
365
|
+
content: { content: 'Fourth' },
|
|
366
|
+
resourceId: 'cross-thread-resource',
|
|
367
|
+
}),
|
|
368
|
+
createSampleMessageV2({
|
|
369
|
+
threadId: 'thread-two',
|
|
370
|
+
content: { content: 'Fifth' },
|
|
371
|
+
resourceId: 'cross-thread-resource',
|
|
372
|
+
}),
|
|
373
|
+
createSampleMessageV2({
|
|
374
|
+
threadId: 'thread-two',
|
|
375
|
+
content: { content: 'Sixth' },
|
|
376
|
+
resourceId: 'cross-thread-resource',
|
|
377
|
+
}),
|
|
354
378
|
|
|
355
|
-
createSampleMessageV2({
|
|
356
|
-
|
|
379
|
+
createSampleMessageV2({
|
|
380
|
+
threadId: 'thread-three',
|
|
381
|
+
content: { content: 'Seventh' },
|
|
382
|
+
resourceId: 'other-resource',
|
|
383
|
+
}),
|
|
384
|
+
createSampleMessageV2({
|
|
385
|
+
threadId: 'thread-three',
|
|
386
|
+
content: { content: 'Eighth' },
|
|
387
|
+
resourceId: 'other-resource',
|
|
388
|
+
}),
|
|
357
389
|
];
|
|
358
390
|
|
|
359
391
|
await store.saveMessages({ messages: messages, format: 'v2' });
|
|
@@ -466,13 +498,88 @@ describe('UpstashStore', () => {
|
|
|
466
498
|
expect(retrievedMessages[0].content).toEqual(messages[0].content);
|
|
467
499
|
});
|
|
468
500
|
|
|
501
|
+
it('should upsert messages: duplicate id+threadId results in update, not duplicate row', async () => {
|
|
502
|
+
const thread = await createSampleThread();
|
|
503
|
+
await store.saveThread({ thread });
|
|
504
|
+
const baseMessage = createSampleMessageV2({
|
|
505
|
+
threadId: thread.id,
|
|
506
|
+
createdAt: new Date(),
|
|
507
|
+
content: { content: 'Original' },
|
|
508
|
+
resourceId: thread.resourceId,
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// Insert the message for the first time
|
|
512
|
+
await store.saveMessages({ messages: [baseMessage], format: 'v2' });
|
|
513
|
+
|
|
514
|
+
// Insert again with the same id and threadId but different content
|
|
515
|
+
const updatedMessage = {
|
|
516
|
+
...createSampleMessageV2({
|
|
517
|
+
threadId: thread.id,
|
|
518
|
+
createdAt: new Date(),
|
|
519
|
+
content: { content: 'Updated' },
|
|
520
|
+
resourceId: thread.resourceId,
|
|
521
|
+
}),
|
|
522
|
+
id: baseMessage.id,
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
await store.saveMessages({ messages: [updatedMessage], format: 'v2' });
|
|
526
|
+
|
|
527
|
+
// Retrieve messages for the thread
|
|
528
|
+
const retrievedMessages = await store.getMessages({ threadId: thread.id, format: 'v2' });
|
|
529
|
+
|
|
530
|
+
// Only one message should exist for that id+threadId
|
|
531
|
+
expect(retrievedMessages.filter(m => m.id === baseMessage.id)).toHaveLength(1);
|
|
532
|
+
|
|
533
|
+
// The content should be the updated one
|
|
534
|
+
expect(retrievedMessages.find(m => m.id === baseMessage.id)?.content.content).toBe('Updated');
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('should upsert messages: duplicate id and different threadid', async () => {
|
|
538
|
+
const thread1 = await createSampleThread();
|
|
539
|
+
const thread2 = await createSampleThread();
|
|
540
|
+
await store.saveThread({ thread: thread1 });
|
|
541
|
+
await store.saveThread({ thread: thread2 });
|
|
542
|
+
|
|
543
|
+
const message = createSampleMessageV2({
|
|
544
|
+
threadId: thread1.id,
|
|
545
|
+
createdAt: new Date(),
|
|
546
|
+
content: { content: 'Thread1 Content' },
|
|
547
|
+
resourceId: thread1.resourceId,
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Insert message into thread1
|
|
551
|
+
await store.saveMessages({ messages: [message], format: 'v2' });
|
|
552
|
+
|
|
553
|
+
// Attempt to insert a message with the same id but different threadId
|
|
554
|
+
const conflictingMessage = {
|
|
555
|
+
...createSampleMessageV2({
|
|
556
|
+
threadId: thread2.id, // different thread
|
|
557
|
+
content: { content: 'Thread2 Content' },
|
|
558
|
+
resourceId: thread2.resourceId,
|
|
559
|
+
}),
|
|
560
|
+
id: message.id,
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// Save should move the message to the new thread
|
|
564
|
+
await store.saveMessages({ messages: [conflictingMessage], format: 'v2' });
|
|
565
|
+
|
|
566
|
+
// Retrieve messages for both threads
|
|
567
|
+
const thread1Messages = await store.getMessages({ threadId: thread1.id, format: 'v2' });
|
|
568
|
+
const thread2Messages = await store.getMessages({ threadId: thread2.id, format: 'v2' });
|
|
569
|
+
|
|
570
|
+
// Thread 1 should NOT have the message with that id
|
|
571
|
+
expect(thread1Messages.find(m => m.id === message.id)).toBeUndefined();
|
|
572
|
+
|
|
573
|
+
// Thread 2 should have the message with that id
|
|
574
|
+
expect(thread2Messages.find(m => m.id === message.id)?.content.content).toBe('Thread2 Content');
|
|
575
|
+
});
|
|
469
576
|
describe('getMessagesPaginated', () => {
|
|
470
577
|
it('should return paginated messages with total count', async () => {
|
|
471
578
|
const thread = createSampleThread();
|
|
472
579
|
await store.saveThread({ thread });
|
|
473
580
|
|
|
474
581
|
const messages = Array.from({ length: 15 }, (_, i) =>
|
|
475
|
-
createSampleMessageV2({ threadId: thread.id, content: `Message ${i + 1}` }),
|
|
582
|
+
createSampleMessageV2({ threadId: thread.id, content: { content: `Message ${i + 1}` } }),
|
|
476
583
|
);
|
|
477
584
|
|
|
478
585
|
await store.saveMessages({ messages, format: 'v2' });
|
|
@@ -512,7 +619,7 @@ describe('UpstashStore', () => {
|
|
|
512
619
|
await store.saveThread({ thread });
|
|
513
620
|
|
|
514
621
|
const messages = Array.from({ length: 10 }, (_, i) => {
|
|
515
|
-
const message = createSampleMessageV2({ threadId: thread.id, content: `Message ${i + 1}` });
|
|
622
|
+
const message = createSampleMessageV2({ threadId: thread.id, content: { content: `Message ${i + 1}` } });
|
|
516
623
|
// Ensure different timestamps
|
|
517
624
|
message.createdAt = new Date(Date.now() + i * 1000);
|
|
518
625
|
return message;
|
|
@@ -545,13 +652,13 @@ describe('UpstashStore', () => {
|
|
|
545
652
|
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
|
|
546
653
|
|
|
547
654
|
const oldMessages = Array.from({ length: 3 }, (_, i) => {
|
|
548
|
-
const message = createSampleMessageV2({ threadId: thread.id, content: `Old Message ${i + 1}` });
|
|
655
|
+
const message = createSampleMessageV2({ threadId: thread.id, content: { content: `Old Message ${i + 1}` } });
|
|
549
656
|
message.createdAt = yesterday;
|
|
550
657
|
return message;
|
|
551
658
|
});
|
|
552
659
|
|
|
553
660
|
const newMessages = Array.from({ length: 4 }, (_, i) => {
|
|
554
|
-
const message = createSampleMessageV2({ threadId: thread.id, content: `New Message ${i + 1}` });
|
|
661
|
+
const message = createSampleMessageV2({ threadId: thread.id, content: { content: `New Message ${i + 1}` } });
|
|
555
662
|
message.createdAt = tomorrow;
|
|
556
663
|
return message;
|
|
557
664
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
2
|
|
|
3
|
+
import type { UpstashVectorFilter } from './filter';
|
|
3
4
|
import { UpstashFilterTranslator } from './filter';
|
|
4
5
|
|
|
5
6
|
describe('UpstashFilterTranslator', () => {
|
|
@@ -22,7 +23,7 @@ describe('UpstashFilterTranslator', () => {
|
|
|
22
23
|
|
|
23
24
|
it('translates nested paths', () => {
|
|
24
25
|
expect(translator.translate({ 'geography.continent': 'Asia' })).toBe("geography.continent = 'Asia'");
|
|
25
|
-
expect(translator.translate({ geography: { continent: 'Asia' } })).toBe("geography.continent = 'Asia'");
|
|
26
|
+
expect(translator.translate({ geography: { continent: 'Asia' } } as any)).toBe("geography.continent = 'Asia'");
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
it('translates comparison operators', () => {
|
|
@@ -65,7 +66,7 @@ describe('UpstashFilterTranslator', () => {
|
|
|
65
66
|
});
|
|
66
67
|
|
|
67
68
|
it('translates complex nested conditions', () => {
|
|
68
|
-
const filter = {
|
|
69
|
+
const filter: UpstashVectorFilter = {
|
|
69
70
|
$and: [
|
|
70
71
|
{ population: { $gte: 1000000 } },
|
|
71
72
|
{ 'geography.continent': 'Asia' },
|
|
@@ -154,7 +155,7 @@ describe('UpstashFilterTranslator', () => {
|
|
|
154
155
|
|
|
155
156
|
describe('complex scenarios', () => {
|
|
156
157
|
it('deeply nested logical operators', () => {
|
|
157
|
-
const filter = {
|
|
158
|
+
const filter: UpstashVectorFilter = {
|
|
158
159
|
$or: [
|
|
159
160
|
{
|
|
160
161
|
$and: [
|
|
@@ -214,7 +215,7 @@ describe('UpstashFilterTranslator', () => {
|
|
|
214
215
|
});
|
|
215
216
|
|
|
216
217
|
it('complex filtering with all operator types', () => {
|
|
217
|
-
const filter = {
|
|
218
|
+
const filter: UpstashVectorFilter = {
|
|
218
219
|
$and: [
|
|
219
220
|
{
|
|
220
221
|
$or: [{ name: { $regex: 'San*' } }, { name: { $regex: 'New*' } }],
|
|
@@ -531,7 +532,7 @@ describe('UpstashFilterTranslator', () => {
|
|
|
531
532
|
});
|
|
532
533
|
|
|
533
534
|
it('throws error for invalid $not operator', () => {
|
|
534
|
-
expect(() => translator.translate({ field: { $not: true } })).toThrow();
|
|
535
|
+
expect(() => translator.translate({ field: { $not: true } } as any)).toThrow();
|
|
535
536
|
});
|
|
536
537
|
|
|
537
538
|
it('throws error for regex operators', () => {
|
|
@@ -539,7 +540,7 @@ describe('UpstashFilterTranslator', () => {
|
|
|
539
540
|
expect(() => translator.translate(filter)).toThrow();
|
|
540
541
|
});
|
|
541
542
|
it('throws error for non-logical operators at top level', () => {
|
|
542
|
-
const invalidFilters = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
|
|
543
|
+
const invalidFilters: any = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
|
|
543
544
|
|
|
544
545
|
invalidFilters.forEach(filter => {
|
|
545
546
|
expect(() => translator.translate(filter)).toThrow(/Invalid top-level operator/);
|
package/src/vector/filter.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
2
|
-
import type {
|
|
2
|
+
import type { OperatorSupport, VectorFilter, OperatorValueMap } from '@mastra/core/vector/filter';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
type UpstashOperatorValueMap = Omit<OperatorValueMap, '$options' | '$elemMatch'> & {
|
|
5
|
+
$contains: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type UpstashVectorFilter = VectorFilter<keyof UpstashOperatorValueMap, UpstashOperatorValueMap>;
|
|
9
|
+
|
|
10
|
+
export class UpstashFilterTranslator extends BaseFilterTranslator<UpstashVectorFilter, string | undefined> {
|
|
5
11
|
protected override getSupportedOperators(): OperatorSupport {
|
|
6
12
|
return {
|
|
7
13
|
...BaseFilterTranslator.DEFAULT_OPERATORS,
|
|
@@ -11,13 +17,13 @@ export class UpstashFilterTranslator extends BaseFilterTranslator {
|
|
|
11
17
|
};
|
|
12
18
|
}
|
|
13
19
|
|
|
14
|
-
translate(filter?:
|
|
20
|
+
translate(filter?: UpstashVectorFilter): string | undefined {
|
|
15
21
|
if (this.isEmpty(filter)) return undefined;
|
|
16
22
|
this.validateFilter(filter);
|
|
17
23
|
return this.translateNode(filter);
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
private translateNode(node:
|
|
26
|
+
private translateNode(node: UpstashVectorFilter, path: string = ''): string {
|
|
21
27
|
if (this.isRegex(node)) {
|
|
22
28
|
throw new Error('Direct regex pattern format is not supported in Upstash');
|
|
23
29
|
}
|
package/src/vector/index.test.ts
CHANGED
|
@@ -1146,7 +1146,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
1146
1146
|
vectorStore.query({
|
|
1147
1147
|
indexName: filterIndexName,
|
|
1148
1148
|
queryVector: createVector(0),
|
|
1149
|
-
filter: { field: { $invalidOp: 'value' } },
|
|
1149
|
+
filter: { field: { $invalidOp: 'value' } as any },
|
|
1150
1150
|
}),
|
|
1151
1151
|
).rejects.toThrow();
|
|
1152
1152
|
});
|
|
@@ -1196,7 +1196,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
1196
1196
|
const results = await vectorStore.query({
|
|
1197
1197
|
indexName: filterIndexName,
|
|
1198
1198
|
queryVector: createVector(0),
|
|
1199
|
-
filter: { $and: { not: 'an array' } },
|
|
1199
|
+
filter: { $and: { not: 'an array' } as any },
|
|
1200
1200
|
});
|
|
1201
1201
|
expect(results.length).toBeGreaterThan(0);
|
|
1202
1202
|
});
|
package/src/vector/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MastraError, ErrorDomain, ErrorCategory } from '@mastra/core/error';
|
|
1
2
|
import { MastraVector } from '@mastra/core/vector';
|
|
2
3
|
import type {
|
|
3
4
|
CreateIndexParams,
|
|
@@ -10,12 +11,14 @@ import type {
|
|
|
10
11
|
UpdateVectorParams,
|
|
11
12
|
UpsertVectorParams,
|
|
12
13
|
} from '@mastra/core/vector';
|
|
13
|
-
import type { VectorFilter } from '@mastra/core/vector/filter';
|
|
14
14
|
import { Index } from '@upstash/vector';
|
|
15
15
|
|
|
16
16
|
import { UpstashFilterTranslator } from './filter';
|
|
17
|
+
import type { UpstashVectorFilter } from './filter';
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
type UpstashQueryVectorParams = QueryVectorParams<UpstashVectorFilter>;
|
|
20
|
+
|
|
21
|
+
export class UpstashVector extends MastraVector<UpstashVectorFilter> {
|
|
19
22
|
private client: Index;
|
|
20
23
|
|
|
21
24
|
/**
|
|
@@ -46,18 +49,30 @@ export class UpstashVector extends MastraVector {
|
|
|
46
49
|
metadata: metadata?.[index],
|
|
47
50
|
}));
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
try {
|
|
53
|
+
await this.client.upsert(points, {
|
|
54
|
+
namespace,
|
|
55
|
+
});
|
|
56
|
+
return generatedIds;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new MastraError(
|
|
59
|
+
{
|
|
60
|
+
id: 'STORAGE_UPSTASH_VECTOR_UPSERT_FAILED',
|
|
61
|
+
domain: ErrorDomain.STORAGE,
|
|
62
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
63
|
+
details: { namespace, vectorCount: vectors.length },
|
|
64
|
+
},
|
|
65
|
+
error,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
53
68
|
}
|
|
54
69
|
|
|
55
70
|
/**
|
|
56
71
|
* Transforms a Mastra vector filter into an Upstash-compatible filter string.
|
|
57
|
-
* @param {
|
|
72
|
+
* @param {UpstashVectorFilter} [filter] - The filter to transform.
|
|
58
73
|
* @returns {string | undefined} The transformed filter string, or undefined if no filter is provided.
|
|
59
74
|
*/
|
|
60
|
-
transformFilter(filter?:
|
|
75
|
+
transformFilter(filter?: UpstashVectorFilter) {
|
|
61
76
|
const translator = new UpstashFilterTranslator();
|
|
62
77
|
return translator.translate(filter);
|
|
63
78
|
}
|
|
@@ -82,25 +97,37 @@ export class UpstashVector extends MastraVector {
|
|
|
82
97
|
topK = 10,
|
|
83
98
|
filter,
|
|
84
99
|
includeVector = false,
|
|
85
|
-
}:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const filterString = this.transformFilter(filter);
|
|
89
|
-
const results = await ns.query({
|
|
90
|
-
topK,
|
|
91
|
-
vector: queryVector,
|
|
92
|
-
includeVectors: includeVector,
|
|
93
|
-
includeMetadata: true,
|
|
94
|
-
...(filterString ? { filter: filterString } : {}),
|
|
95
|
-
});
|
|
100
|
+
}: UpstashQueryVectorParams): Promise<QueryResult[]> {
|
|
101
|
+
try {
|
|
102
|
+
const ns = this.client.namespace(namespace);
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
const filterString = this.transformFilter(filter);
|
|
105
|
+
const results = await ns.query({
|
|
106
|
+
topK,
|
|
107
|
+
vector: queryVector,
|
|
108
|
+
includeVectors: includeVector,
|
|
109
|
+
includeMetadata: true,
|
|
110
|
+
...(filterString ? { filter: filterString } : {}),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Map the results to our expected format
|
|
114
|
+
return (results || []).map(result => ({
|
|
115
|
+
id: `${result.id}`,
|
|
116
|
+
score: result.score,
|
|
117
|
+
metadata: result.metadata,
|
|
118
|
+
...(includeVector && { vector: result.vector || [] }),
|
|
119
|
+
}));
|
|
120
|
+
} catch (error) {
|
|
121
|
+
throw new MastraError(
|
|
122
|
+
{
|
|
123
|
+
id: 'STORAGE_UPSTASH_VECTOR_QUERY_FAILED',
|
|
124
|
+
domain: ErrorDomain.STORAGE,
|
|
125
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
126
|
+
details: { namespace, topK },
|
|
127
|
+
},
|
|
128
|
+
error,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
104
131
|
}
|
|
105
132
|
|
|
106
133
|
/**
|
|
@@ -108,8 +135,19 @@ export class UpstashVector extends MastraVector {
|
|
|
108
135
|
* @returns {Promise<string[]>} A promise that resolves to a list of index names.
|
|
109
136
|
*/
|
|
110
137
|
async listIndexes(): Promise<string[]> {
|
|
111
|
-
|
|
112
|
-
|
|
138
|
+
try {
|
|
139
|
+
const indexes = await this.client.listNamespaces();
|
|
140
|
+
return indexes.filter(Boolean);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
throw new MastraError(
|
|
143
|
+
{
|
|
144
|
+
id: 'STORAGE_UPSTASH_VECTOR_LIST_INDEXES_FAILED',
|
|
145
|
+
domain: ErrorDomain.STORAGE,
|
|
146
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
147
|
+
},
|
|
148
|
+
error,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
113
151
|
}
|
|
114
152
|
|
|
115
153
|
/**
|
|
@@ -119,13 +157,25 @@ export class UpstashVector extends MastraVector {
|
|
|
119
157
|
* @returns A promise that resolves to the index statistics including dimension, count and metric
|
|
120
158
|
*/
|
|
121
159
|
async describeIndex({ indexName: namespace }: DescribeIndexParams): Promise<IndexStats> {
|
|
122
|
-
|
|
160
|
+
try {
|
|
161
|
+
const info = await this.client.info();
|
|
123
162
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
163
|
+
return {
|
|
164
|
+
dimension: info.dimension,
|
|
165
|
+
count: info.namespaces?.[namespace]?.vectorCount || 0,
|
|
166
|
+
metric: info?.similarityFunction?.toLowerCase() as 'cosine' | 'euclidean' | 'dotproduct',
|
|
167
|
+
};
|
|
168
|
+
} catch (error) {
|
|
169
|
+
throw new MastraError(
|
|
170
|
+
{
|
|
171
|
+
id: 'STORAGE_UPSTASH_VECTOR_DESCRIBE_INDEX_FAILED',
|
|
172
|
+
domain: ErrorDomain.STORAGE,
|
|
173
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
174
|
+
details: { namespace },
|
|
175
|
+
},
|
|
176
|
+
error,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
129
179
|
}
|
|
130
180
|
|
|
131
181
|
/**
|
|
@@ -137,7 +187,15 @@ export class UpstashVector extends MastraVector {
|
|
|
137
187
|
try {
|
|
138
188
|
await this.client.deleteNamespace(namespace);
|
|
139
189
|
} catch (error) {
|
|
140
|
-
|
|
190
|
+
throw new MastraError(
|
|
191
|
+
{
|
|
192
|
+
id: 'STORAGE_UPSTASH_VECTOR_DELETE_INDEX_FAILED',
|
|
193
|
+
domain: ErrorDomain.STORAGE,
|
|
194
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
195
|
+
details: { namespace },
|
|
196
|
+
},
|
|
197
|
+
error,
|
|
198
|
+
);
|
|
141
199
|
}
|
|
142
200
|
}
|
|
143
201
|
|
|
@@ -162,7 +220,19 @@ export class UpstashVector extends MastraVector {
|
|
|
162
220
|
if (!update.vector && update.metadata) {
|
|
163
221
|
throw new Error('Both vector and metadata must be provided for an update');
|
|
164
222
|
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
throw new MastraError(
|
|
225
|
+
{
|
|
226
|
+
id: 'STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED',
|
|
227
|
+
domain: ErrorDomain.STORAGE,
|
|
228
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
229
|
+
details: { namespace, id },
|
|
230
|
+
},
|
|
231
|
+
error,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
165
234
|
|
|
235
|
+
try {
|
|
166
236
|
const updatePayload: any = { id: id };
|
|
167
237
|
if (update.vector) {
|
|
168
238
|
updatePayload.vector = update.vector;
|
|
@@ -180,8 +250,16 @@ export class UpstashVector extends MastraVector {
|
|
|
180
250
|
await this.client.upsert(points, {
|
|
181
251
|
namespace,
|
|
182
252
|
});
|
|
183
|
-
} catch (error
|
|
184
|
-
throw new
|
|
253
|
+
} catch (error) {
|
|
254
|
+
throw new MastraError(
|
|
255
|
+
{
|
|
256
|
+
id: 'STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED',
|
|
257
|
+
domain: ErrorDomain.STORAGE,
|
|
258
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
259
|
+
details: { namespace, id },
|
|
260
|
+
},
|
|
261
|
+
error,
|
|
262
|
+
);
|
|
185
263
|
}
|
|
186
264
|
}
|
|
187
265
|
|
|
@@ -198,7 +276,16 @@ export class UpstashVector extends MastraVector {
|
|
|
198
276
|
namespace,
|
|
199
277
|
});
|
|
200
278
|
} catch (error) {
|
|
201
|
-
|
|
279
|
+
const mastraError = new MastraError(
|
|
280
|
+
{
|
|
281
|
+
id: 'STORAGE_UPSTASH_VECTOR_DELETE_VECTOR_FAILED',
|
|
282
|
+
domain: ErrorDomain.STORAGE,
|
|
283
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
284
|
+
details: { namespace, id },
|
|
285
|
+
},
|
|
286
|
+
error,
|
|
287
|
+
);
|
|
288
|
+
this.logger?.error(mastraError.toString());
|
|
202
289
|
}
|
|
203
290
|
}
|
|
204
291
|
}
|