@mastra/upstash 0.1.13-alpha.0 → 0.2.0-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.
@@ -1,23 +1,23 @@
1
1
 
2
- > @mastra/upstash@0.1.13-alpha.0 build /home/runner/work/mastra/mastra/stores/upstash
2
+ > @mastra/upstash@0.2.0-alpha.1 build /home/runner/work/mastra/mastra/stores/upstash
3
3
  > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.4.0
8
8
  TSC Build start
9
- TSC ⚡️ Build success in 9737ms
9
+ TSC ⚡️ Build success in 9696ms
10
10
  DTS Build start
11
11
  CLI Target: es2022
12
12
  Analysis will use the bundled TypeScript version 5.8.2
13
13
  Writing package typings: /home/runner/work/mastra/mastra/stores/upstash/dist/_tsup-dts-rollup.d.ts
14
14
  Analysis will use the bundled TypeScript version 5.8.2
15
15
  Writing package typings: /home/runner/work/mastra/mastra/stores/upstash/dist/_tsup-dts-rollup.d.cts
16
- DTS ⚡️ Build success in 10952ms
16
+ DTS ⚡️ Build success in 10580ms
17
17
  CLI Cleaning output folder
18
18
  ESM Build start
19
19
  CJS Build start
20
- CJS dist/index.cjs 16.31 KB
21
- CJS ⚡️ Build success in 711ms
22
- ESM dist/index.js 16.19 KB
23
- ESM ⚡️ Build success in 717ms
20
+ ESM dist/index.js 17.07 KB
21
+ ESM ⚡️ Build success in 746ms
22
+ CJS dist/index.cjs 17.20 KB
23
+ CJS ⚡️ Build success in 747ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @mastra/upstash
2
2
 
3
+ ## 0.2.0-alpha.1
4
+
5
+ ### Minor Changes
6
+
7
+ - c0b2496: Added new operations implementations for MastraVector methods in upstash store
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [6794797]
12
+ - @mastra/core@0.6.4-alpha.1
13
+
3
14
  ## 0.1.13-alpha.0
4
15
 
5
16
  ### Patch Changes
@@ -126,6 +126,11 @@ declare class UpstashVector extends MastraVector {
126
126
  metric: "cosine" | "euclidean" | "dotproduct";
127
127
  }>;
128
128
  deleteIndex(indexName: string): Promise<void>;
129
+ updateIndexById(indexName: string, id: string, update: {
130
+ vector?: number[];
131
+ metadata?: Record<string, any>;
132
+ }): Promise<void>;
133
+ deleteIndexById(indexName: string, id: string): Promise<void>;
129
134
  }
130
135
  export { UpstashVector }
131
136
  export { UpstashVector as UpstashVector_alias_1 }
@@ -126,6 +126,11 @@ declare class UpstashVector extends MastraVector {
126
126
  metric: "cosine" | "euclidean" | "dotproduct";
127
127
  }>;
128
128
  deleteIndex(indexName: string): Promise<void>;
129
+ updateIndexById(indexName: string, id: string, update: {
130
+ vector?: number[];
131
+ metadata?: Record<string, any>;
132
+ }): Promise<void>;
133
+ deleteIndexById(indexName: string, id: string): Promise<void>;
129
134
  }
130
135
  export { UpstashVector }
131
136
  export { UpstashVector as UpstashVector_alias_1 }
package/dist/index.cjs CHANGED
@@ -474,6 +474,38 @@ var UpstashVector = class extends vector.MastraVector {
474
474
  console.error("Failed to delete namespace:", error);
475
475
  }
476
476
  }
477
+ async updateIndexById(indexName, id, update) {
478
+ if (!update.vector && !update.metadata) {
479
+ throw new Error("No update data provided");
480
+ }
481
+ if (!update.vector && update.metadata) {
482
+ throw new Error("Both vector and metadata must be provided for an update");
483
+ }
484
+ const updatePayload = { id };
485
+ if (update.vector) {
486
+ updatePayload.vector = update.vector;
487
+ }
488
+ if (update.metadata) {
489
+ updatePayload.metadata = update.metadata;
490
+ }
491
+ const points = {
492
+ id: updatePayload.id,
493
+ vector: updatePayload.vector,
494
+ metadata: updatePayload.metadata
495
+ };
496
+ await this.client.upsert(points, {
497
+ namespace: indexName
498
+ });
499
+ }
500
+ async deleteIndexById(indexName, id) {
501
+ try {
502
+ await this.client.delete(id, {
503
+ namespace: indexName
504
+ });
505
+ } catch (error) {
506
+ console.error("Failed to delete index by ID:", error);
507
+ }
508
+ }
477
509
  };
478
510
 
479
511
  exports.UpstashStore = UpstashStore;
package/dist/index.js CHANGED
@@ -472,6 +472,38 @@ var UpstashVector = class extends MastraVector {
472
472
  console.error("Failed to delete namespace:", error);
473
473
  }
474
474
  }
475
+ async updateIndexById(indexName, id, update) {
476
+ if (!update.vector && !update.metadata) {
477
+ throw new Error("No update data provided");
478
+ }
479
+ if (!update.vector && update.metadata) {
480
+ throw new Error("Both vector and metadata must be provided for an update");
481
+ }
482
+ const updatePayload = { id };
483
+ if (update.vector) {
484
+ updatePayload.vector = update.vector;
485
+ }
486
+ if (update.metadata) {
487
+ updatePayload.metadata = update.metadata;
488
+ }
489
+ const points = {
490
+ id: updatePayload.id,
491
+ vector: updatePayload.vector,
492
+ metadata: updatePayload.metadata
493
+ };
494
+ await this.client.upsert(points, {
495
+ namespace: indexName
496
+ });
497
+ }
498
+ async deleteIndexById(indexName, id) {
499
+ try {
500
+ await this.client.delete(id, {
501
+ namespace: indexName
502
+ });
503
+ } catch (error) {
504
+ console.error("Failed to delete index by ID:", error);
505
+ }
506
+ }
475
507
  };
476
508
 
477
509
  export { UpstashStore, UpstashVector };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/upstash",
3
- "version": "0.1.13-alpha.0",
3
+ "version": "0.2.0-alpha.1",
4
4
  "description": "Upstash provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,11 +21,12 @@
21
21
  "dependencies": {
22
22
  "@upstash/redis": "^1.34.5",
23
23
  "@upstash/vector": "^1.2.1",
24
- "@mastra/core": "^0.6.4-alpha.0"
24
+ "@mastra/core": "^0.6.4-alpha.1"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@microsoft/api-extractor": "^7.52.1",
28
28
  "@types/node": "^22.13.10",
29
+ "dotenv": "^16.4.7",
29
30
  "eslint": "^9.22.0",
30
31
  "tsup": "^8.4.0",
31
32
  "typescript": "^5.8.2",
@@ -1,6 +1,11 @@
1
+ import dotenv from 'dotenv';
2
+
1
3
  import { describe, it, expect, beforeAll, afterAll, beforeEach, vi, afterEach } from 'vitest';
2
4
 
3
5
  import { UpstashVector } from './';
6
+ import type { QueryResult } from '@mastra/core';
7
+
8
+ dotenv.config();
4
9
 
5
10
  function waitUntilVectorsIndexed(vector: UpstashVector, indexName: string, expectedCount: number) {
6
11
  return new Promise((resolve, reject) => {
@@ -98,19 +103,145 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
98
103
  }, 5000000);
99
104
 
100
105
  it('should query vectors and return vector in results', async () => {
101
- const results = await vectorStore.query({ indexName: testIndexName, queryVector: createVector(0, 0.9), topK: 3 });
106
+ const results = await vectorStore.query({
107
+ indexName: testIndexName,
108
+ queryVector: createVector(0, 0.9),
109
+ topK: 3,
110
+ includeVector: true,
111
+ });
102
112
  expect(results).toHaveLength(3);
103
- expect(results?.[0]?.vector).toBeDefined();
104
- expect(results?.[0]?.vector).toHaveLength(VECTOR_DIMENSION);
105
- expect(results?.[1]?.vector).toBeDefined();
106
- expect(results?.[1]?.vector).toHaveLength(VECTOR_DIMENSION);
107
- expect(results?.[2]?.vector).toBeDefined();
108
- expect(results?.[2]?.vector).toHaveLength(VECTOR_DIMENSION);
113
+ results.forEach(result => {
114
+ expect(result.vector).toBeDefined();
115
+ expect(result.vector).toHaveLength(VECTOR_DIMENSION);
116
+ });
117
+ });
118
+
119
+ describe('Vector update operations', () => {
120
+ const testVectors = [createVector(0, 1.0), createVector(1, 1.0), createVector(2, 1.0)];
121
+
122
+ const testIndexName = 'test-index';
123
+
124
+ afterEach(async () => {
125
+ await vectorStore.deleteIndex(testIndexName);
126
+ });
127
+
128
+ it('should update the vector by id', async () => {
129
+ const ids = await vectorStore.upsert({ indexName: testIndexName, vectors: testVectors });
130
+ expect(ids).toHaveLength(3);
131
+
132
+ const idToBeUpdated = ids[0];
133
+ const newVector = createVector(0, 4.0);
134
+ const newMetaData = {
135
+ test: 'updates',
136
+ };
137
+
138
+ const update = {
139
+ vector: newVector,
140
+ metadata: newMetaData,
141
+ };
142
+
143
+ await vectorStore.updateIndexById(testIndexName, idToBeUpdated, update);
144
+
145
+ await waitUntilVectorsIndexed(vectorStore, testIndexName, 3);
146
+
147
+ const results: QueryResult[] = await vectorStore.query({
148
+ indexName: testIndexName,
149
+ queryVector: newVector,
150
+ topK: 2,
151
+ includeVector: true,
152
+ });
153
+ expect(results[0]?.id).toBe(idToBeUpdated);
154
+ expect(results[0]?.vector).toEqual(newVector);
155
+ expect(results[0]?.metadata).toEqual(newMetaData);
156
+ }, 500000);
157
+
158
+ it('should only update the metadata by id', async () => {
159
+ const ids = await vectorStore.upsert({ indexName: testIndexName, vectors: testVectors });
160
+ expect(ids).toHaveLength(3);
161
+
162
+ const idToBeUpdated = ids[0];
163
+ const newMetaData = {
164
+ test: 'updates',
165
+ };
166
+
167
+ const update = {
168
+ metadata: newMetaData,
169
+ };
170
+
171
+ await expect(vectorStore.updateIndexById(testIndexName, 'id', update)).rejects.toThrow(
172
+ 'Both vector and metadata must be provided for an update',
173
+ );
174
+ });
175
+
176
+ it('should only update vector embeddings by id', async () => {
177
+ const ids = await vectorStore.upsert({ indexName: testIndexName, vectors: testVectors });
178
+ expect(ids).toHaveLength(3);
179
+
180
+ const idToBeUpdated = ids[0];
181
+ const newVector = createVector(0, 4.0);
182
+
183
+ const update = {
184
+ vector: newVector,
185
+ };
186
+
187
+ await vectorStore.updateIndexById(testIndexName, idToBeUpdated, update);
188
+
189
+ await waitUntilVectorsIndexed(vectorStore, testIndexName, 3);
190
+
191
+ const results: QueryResult[] = await vectorStore.query({
192
+ indexName: testIndexName,
193
+ queryVector: newVector,
194
+ topK: 2,
195
+ includeVector: true,
196
+ });
197
+ expect(results[0]?.id).toBe(idToBeUpdated);
198
+ expect(results[0]?.vector).toEqual(newVector);
199
+ }, 500000);
200
+
201
+ it('should throw exception when no updates are given', async () => {
202
+ await expect(vectorStore.updateIndexById(testIndexName, 'id', {})).rejects.toThrow('No update data provided');
203
+ });
204
+ });
205
+
206
+ describe('Vector delete operations', () => {
207
+ const testVectors = [createVector(0, 1.0), createVector(1, 1.0), createVector(2, 1.0)];
208
+
209
+ afterEach(async () => {
210
+ await vectorStore.deleteIndex(testIndexName);
211
+ });
212
+
213
+ it('should delete the vector by id', async () => {
214
+ const ids = await vectorStore.upsert({ indexName: testIndexName, vectors: testVectors });
215
+ expect(ids).toHaveLength(3);
216
+ const idToBeDeleted = ids[0];
217
+
218
+ await vectorStore.deleteIndexById(testIndexName, idToBeDeleted);
219
+
220
+ const results: QueryResult[] = await vectorStore.query({
221
+ indexName: testIndexName,
222
+ queryVector: createVector(0, 1.0),
223
+ topK: 2,
224
+ });
225
+
226
+ expect(results).toHaveLength(2);
227
+ expect(results.map(res => res.id)).not.toContain(idToBeDeleted);
228
+ });
109
229
  });
110
230
  });
111
231
  describe('Index Operations', () => {
232
+ const createVector = (primaryDimension: number, value: number = 1.0): number[] => {
233
+ const vector = new Array(VECTOR_DIMENSION).fill(0);
234
+ vector[primaryDimension] = value;
235
+ // Normalize the vector for cosine similarity
236
+ const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
237
+ return vector.map(val => val / magnitude);
238
+ };
112
239
  it('should create and list an index', async () => {
113
- await vectorStore.createIndex({ indexName: testIndexName, dimension: 3, metric: 'cosine' });
240
+ // since, we do not have to create index explictly in case of upstash. Upserts are enough
241
+ // for testing the listIndexes() function
242
+ // await vectorStore.createIndex({ indexName: testIndexName, dimension: 3, metric: 'cosine' });
243
+ const ids = await vectorStore.upsert({ indexName: testIndexName, vectors: [createVector(0, 1.0)] });
244
+ expect(ids).toHaveLength(1);
114
245
  const indexes = await vectorStore.listIndexes();
115
246
  expect(indexes).toEqual([testIndexName]);
116
247
  });
@@ -1068,10 +1199,6 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
1068
1199
 
1069
1200
  let warnSpy;
1070
1201
 
1071
- beforeAll(async () => {
1072
- await vectorStore.createIndex({ indexName: indexName, dimension: 3 });
1073
- });
1074
-
1075
1202
  afterAll(async () => {
1076
1203
  await vectorStore.deleteIndex(indexName);
1077
1204
  await vectorStore.deleteIndex(indexName2);
@@ -1086,16 +1213,16 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
1086
1213
  await vectorStore.deleteIndex(indexName2);
1087
1214
  });
1088
1215
 
1089
- it('should show deprecation warning when using individual args for createIndex', async () => {
1090
- await vectorStore.createIndex(indexName2, 3, 'cosine');
1091
-
1092
- expect(warnSpy).toHaveBeenCalledWith(
1093
- expect.stringContaining('Deprecation Warning: Passing individual arguments to createIndex() is deprecated'),
1094
- );
1095
- });
1216
+ const createVector = (primaryDimension: number, value: number = 1.0): number[] => {
1217
+ const vector = new Array(VECTOR_DIMENSION).fill(0);
1218
+ vector[primaryDimension] = value;
1219
+ // Normalize the vector for cosine similarity
1220
+ const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
1221
+ return vector.map(val => val / magnitude);
1222
+ };
1096
1223
 
1097
1224
  it('should show deprecation warning when using individual args for upsert', async () => {
1098
- await vectorStore.upsert(indexName, [[1, 2, 3]], [{ test: 'data' }]);
1225
+ await vectorStore.upsert(indexName, [createVector(0, 2)], [{ test: 'data' }]);
1099
1226
 
1100
1227
  expect(warnSpy).toHaveBeenCalledWith(
1101
1228
  expect.stringContaining('Deprecation Warning: Passing individual arguments to upsert() is deprecated'),
@@ -1103,7 +1230,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
1103
1230
  });
1104
1231
 
1105
1232
  it('should show deprecation warning when using individual args for query', async () => {
1106
- await vectorStore.query(indexName, [1, 2, 3], 5);
1233
+ await vectorStore.query(indexName, createVector(0, 2), 5);
1107
1234
 
1108
1235
  expect(warnSpy).toHaveBeenCalledWith(
1109
1236
  expect.stringContaining('Deprecation Warning: Passing individual arguments to query() is deprecated'),
@@ -1113,7 +1240,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
1113
1240
  it('should not show deprecation warning when using object param for query', async () => {
1114
1241
  await vectorStore.query({
1115
1242
  indexName,
1116
- queryVector: [1, 2, 3],
1243
+ queryVector: createVector(0, 2),
1117
1244
  topK: 5,
1118
1245
  });
1119
1246
 
@@ -1133,7 +1260,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
1133
1260
  it('should not show deprecation warning when using object param for upsert', async () => {
1134
1261
  await vectorStore.upsert({
1135
1262
  indexName,
1136
- vectors: [[1, 2, 3]],
1263
+ vectors: [createVector(0, 2)],
1137
1264
  metadata: [{ test: 'data' }],
1138
1265
  });
1139
1266
 
@@ -1142,7 +1269,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
1142
1269
 
1143
1270
  it('should maintain backward compatibility with individual args', async () => {
1144
1271
  // Query
1145
- const queryResults = await vectorStore.query(indexName, [1, 2, 3], 5);
1272
+ const queryResults = await vectorStore.query(indexName, createVector(0, 2), 5);
1146
1273
  expect(Array.isArray(queryResults)).toBe(true);
1147
1274
 
1148
1275
  // CreateIndex
@@ -1151,7 +1278,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
1151
1278
  // Upsert
1152
1279
  const upsertResults = await vectorStore.upsert({
1153
1280
  indexName,
1154
- vectors: [[1, 2, 3]],
1281
+ vectors: [createVector(0, 2)],
1155
1282
  metadata: [{ test: 'data' }],
1156
1283
  });
1157
1284
  expect(Array.isArray(upsertResults)).toBe(true);
@@ -97,4 +97,51 @@ export class UpstashVector extends MastraVector {
97
97
  console.error('Failed to delete namespace:', error);
98
98
  }
99
99
  }
100
+
101
+ async updateIndexById(
102
+ indexName: string,
103
+ id: string,
104
+ update: {
105
+ vector?: number[];
106
+ metadata?: Record<string, any>;
107
+ },
108
+ ): Promise<void> {
109
+ if (!update.vector && !update.metadata) {
110
+ throw new Error('No update data provided');
111
+ }
112
+
113
+ // The upstash client throws an exception as: 'This index requires dense vectors' when
114
+ // only metadata is present in the update object.
115
+ if (!update.vector && update.metadata) {
116
+ throw new Error('Both vector and metadata must be provided for an update');
117
+ }
118
+
119
+ const updatePayload: any = { id: id };
120
+ if (update.vector) {
121
+ updatePayload.vector = update.vector;
122
+ }
123
+ if (update.metadata) {
124
+ updatePayload.metadata = update.metadata;
125
+ }
126
+
127
+ const points = {
128
+ id: updatePayload.id,
129
+ vector: updatePayload.vector,
130
+ metadata: updatePayload.metadata,
131
+ };
132
+
133
+ await this.client.upsert(points, {
134
+ namespace: indexName,
135
+ });
136
+ }
137
+
138
+ async deleteIndexById(indexName: string, id: string): Promise<void> {
139
+ try {
140
+ await this.client.delete(id, {
141
+ namespace: indexName,
142
+ });
143
+ } catch (error) {
144
+ console.error('Failed to delete index by ID:', error);
145
+ }
146
+ }
100
147
  }