@mastra/upstash 0.1.6-alpha.0 → 0.1.6-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/dist/index.cjs ADDED
@@ -0,0 +1,480 @@
1
+ 'use strict';
2
+
3
+ var storage = require('@mastra/core/storage');
4
+ var redis = require('@upstash/redis');
5
+ var vector = require('@mastra/core/vector');
6
+ var vector$1 = require('@upstash/vector');
7
+ var filter = require('@mastra/core/vector/filter');
8
+
9
+ // src/storage/index.ts
10
+ var UpstashStore = class extends storage.MastraStorage {
11
+ batchInsert(_input) {
12
+ throw new Error("Method not implemented.");
13
+ }
14
+ getEvalsByAgentName(_agentName, _type) {
15
+ throw new Error("Method not implemented.");
16
+ }
17
+ getTraces(_input) {
18
+ throw new Error("Method not implemented.");
19
+ }
20
+ redis;
21
+ constructor(config) {
22
+ super({ name: "Upstash" });
23
+ this.redis = new redis.Redis({
24
+ url: config.url,
25
+ token: config.token
26
+ });
27
+ }
28
+ getKey(tableName, keys) {
29
+ const keyParts = Object.entries(keys).map(([key, value]) => `${key}:${value}`);
30
+ return `${tableName}:${keyParts.join(":")}`;
31
+ }
32
+ ensureDate(date) {
33
+ if (!date) return void 0;
34
+ return date instanceof Date ? date : new Date(date);
35
+ }
36
+ serializeDate(date) {
37
+ if (!date) return void 0;
38
+ const dateObj = this.ensureDate(date);
39
+ return dateObj?.toISOString();
40
+ }
41
+ async createTable({
42
+ tableName,
43
+ schema
44
+ }) {
45
+ await this.redis.set(`schema:${tableName}`, schema);
46
+ }
47
+ async clearTable({ tableName }) {
48
+ const pattern = `${tableName}:*`;
49
+ const keys = await this.redis.keys(pattern);
50
+ if (keys.length > 0) {
51
+ await this.redis.del(...keys);
52
+ }
53
+ }
54
+ async insert({ tableName, record }) {
55
+ let key;
56
+ if (tableName === storage.MastraStorage.TABLE_MESSAGES) {
57
+ key = this.getKey(tableName, { threadId: record.threadId, id: record.id });
58
+ } else {
59
+ key = this.getKey(tableName, { id: record.id });
60
+ }
61
+ const processedRecord = {
62
+ ...record,
63
+ createdAt: this.serializeDate(record.createdAt),
64
+ updatedAt: this.serializeDate(record.updatedAt)
65
+ };
66
+ await this.redis.set(key, processedRecord);
67
+ }
68
+ async load({ tableName, keys }) {
69
+ const key = this.getKey(tableName, keys);
70
+ const data = await this.redis.get(key);
71
+ return data || null;
72
+ }
73
+ async getThreadById({ threadId }) {
74
+ const thread = await this.load({
75
+ tableName: storage.MastraStorage.TABLE_THREADS,
76
+ keys: { id: threadId }
77
+ });
78
+ if (!thread) return null;
79
+ return {
80
+ ...thread,
81
+ createdAt: this.ensureDate(thread.createdAt),
82
+ updatedAt: this.ensureDate(thread.updatedAt),
83
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
84
+ };
85
+ }
86
+ async getThreadsByResourceId({ resourceId }) {
87
+ const pattern = `${storage.MastraStorage.TABLE_THREADS}:*`;
88
+ const keys = await this.redis.keys(pattern);
89
+ const threads = await Promise.all(
90
+ keys.map(async (key) => {
91
+ const data = await this.redis.get(key);
92
+ return data;
93
+ })
94
+ );
95
+ return threads.filter((thread) => thread && thread.resourceId === resourceId).map((thread) => ({
96
+ ...thread,
97
+ createdAt: this.ensureDate(thread.createdAt),
98
+ updatedAt: this.ensureDate(thread.updatedAt),
99
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
100
+ }));
101
+ }
102
+ async saveThread({ thread }) {
103
+ await this.insert({
104
+ tableName: storage.MastraStorage.TABLE_THREADS,
105
+ record: thread
106
+ });
107
+ return thread;
108
+ }
109
+ async updateThread({
110
+ id,
111
+ title,
112
+ metadata
113
+ }) {
114
+ const thread = await this.getThreadById({ threadId: id });
115
+ if (!thread) {
116
+ throw new Error(`Thread ${id} not found`);
117
+ }
118
+ const updatedThread = {
119
+ ...thread,
120
+ title,
121
+ metadata: {
122
+ ...thread.metadata,
123
+ ...metadata
124
+ }
125
+ };
126
+ await this.__saveThread({ thread: updatedThread });
127
+ return updatedThread;
128
+ }
129
+ async deleteThread({ threadId }) {
130
+ const key = this.getKey(storage.MastraStorage.TABLE_THREADS, { id: threadId });
131
+ await this.redis.del(key);
132
+ }
133
+ getMessageKey(threadId, messageId) {
134
+ return this.getKey(storage.MastraStorage.TABLE_MESSAGES, { threadId, id: messageId });
135
+ }
136
+ getThreadMessagesKey(threadId) {
137
+ return `thread:${threadId}:messages`;
138
+ }
139
+ async saveMessages({ messages }) {
140
+ if (messages.length === 0) return [];
141
+ const pipeline = this.redis.pipeline();
142
+ const messagesWithIndex = messages.map((message, index) => ({
143
+ ...message,
144
+ _index: index
145
+ }));
146
+ for (const message of messagesWithIndex) {
147
+ const key = this.getMessageKey(message.threadId, message.id);
148
+ const score = message._index !== void 0 ? message._index : new Date(message.createdAt).getTime();
149
+ pipeline.set(key, message);
150
+ pipeline.zadd(this.getThreadMessagesKey(message.threadId), {
151
+ score,
152
+ member: message.id
153
+ });
154
+ }
155
+ await pipeline.exec();
156
+ return messages;
157
+ }
158
+ async getMessages({ threadId, selectBy }) {
159
+ const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
160
+ const messageIds = /* @__PURE__ */ new Set();
161
+ const threadMessagesKey = this.getThreadMessagesKey(threadId);
162
+ if (limit === 0 && !selectBy?.include) {
163
+ return [];
164
+ }
165
+ if (selectBy?.include?.length) {
166
+ for (const item of selectBy.include) {
167
+ messageIds.add(item.id);
168
+ if (item.withPreviousMessages || item.withNextMessages) {
169
+ const rank = await this.redis.zrank(threadMessagesKey, item.id);
170
+ if (rank === null) continue;
171
+ if (item.withPreviousMessages) {
172
+ const start = Math.max(0, rank - item.withPreviousMessages);
173
+ const prevIds = rank === 0 ? [] : await this.redis.zrange(threadMessagesKey, start, rank - 1);
174
+ prevIds.forEach((id) => messageIds.add(id));
175
+ }
176
+ if (item.withNextMessages) {
177
+ const nextIds = await this.redis.zrange(threadMessagesKey, rank + 1, rank + item.withNextMessages);
178
+ nextIds.forEach((id) => messageIds.add(id));
179
+ }
180
+ }
181
+ }
182
+ }
183
+ const latestIds = limit === 0 ? [] : await this.redis.zrange(threadMessagesKey, -limit, -1);
184
+ latestIds.forEach((id) => messageIds.add(id));
185
+ const messages = (await Promise.all(
186
+ Array.from(messageIds).map(
187
+ async (id) => this.redis.get(this.getMessageKey(threadId, id))
188
+ )
189
+ )).filter((msg) => msg !== null);
190
+ const messageOrder = await this.redis.zrange(threadMessagesKey, 0, -1);
191
+ messages.sort((a, b) => messageOrder.indexOf(a.id) - messageOrder.indexOf(b.id));
192
+ return messages.map(({ _index, ...message }) => message);
193
+ }
194
+ async persistWorkflowSnapshot(params) {
195
+ const { namespace, workflowName, runId, snapshot } = params;
196
+ const key = this.getKey(storage.MastraStorage.TABLE_WORKFLOW_SNAPSHOT, {
197
+ namespace,
198
+ workflow_name: workflowName,
199
+ run_id: runId
200
+ });
201
+ await this.redis.set(key, snapshot);
202
+ }
203
+ async loadWorkflowSnapshot(params) {
204
+ const { namespace, workflowName, runId } = params;
205
+ const key = this.getKey(storage.MastraStorage.TABLE_WORKFLOW_SNAPSHOT, {
206
+ namespace,
207
+ workflow_name: workflowName,
208
+ run_id: runId
209
+ });
210
+ const data = await this.redis.get(key);
211
+ return data || null;
212
+ }
213
+ async close() {
214
+ }
215
+ };
216
+ var UpstashFilterTranslator = class extends filter.BaseFilterTranslator {
217
+ getSupportedOperators() {
218
+ return {
219
+ ...filter.BaseFilterTranslator.DEFAULT_OPERATORS,
220
+ array: ["$in", "$nin", "$all"],
221
+ regex: ["$regex"],
222
+ custom: ["$contains"]
223
+ };
224
+ }
225
+ translate(filter) {
226
+ if (this.isEmpty(filter)) return void 0;
227
+ this.validateFilter(filter);
228
+ return this.translateNode(filter);
229
+ }
230
+ translateNode(node, path = "") {
231
+ if (this.isRegex(node)) {
232
+ throw new Error("Direct regex pattern format is not supported in Upstash");
233
+ }
234
+ if (node === null || node === void 0) {
235
+ throw new Error("Filtering for null/undefined values is not supported by Upstash Vector");
236
+ }
237
+ if (this.isPrimitive(node)) {
238
+ if (node === null || node === void 0) {
239
+ throw new Error("Filtering for null/undefined values is not supported by Upstash Vector");
240
+ }
241
+ return this.formatComparison(path, "=", node);
242
+ }
243
+ if (Array.isArray(node)) {
244
+ if (node.length === 0) {
245
+ return "(HAS FIELD empty AND HAS NOT FIELD empty)";
246
+ }
247
+ return `${path} IN (${this.formatArray(node)})`;
248
+ }
249
+ const entries = Object.entries(node);
250
+ const conditions = [];
251
+ for (const [key, value] of entries) {
252
+ const newPath = path ? `${path}.${key}` : key;
253
+ if (this.isOperator(key)) {
254
+ conditions.push(this.translateOperator(key, value, path));
255
+ } else if (typeof value === "object" && value !== null) {
256
+ conditions.push(this.translateNode(value, newPath));
257
+ } else if (value === null || value === void 0) {
258
+ throw new Error("Filtering for null/undefined values is not supported by Upstash Vector");
259
+ } else {
260
+ conditions.push(this.formatComparison(newPath, "=", value));
261
+ }
262
+ }
263
+ return conditions.length > 1 ? `(${conditions.join(" AND ")})` : conditions[0] ?? "";
264
+ }
265
+ COMPARISON_OPS = {
266
+ $eq: "=",
267
+ $ne: "!=",
268
+ $gt: ">",
269
+ $gte: ">=",
270
+ $lt: "<",
271
+ $lte: "<="
272
+ };
273
+ translateOperator(operator, value, path) {
274
+ if (this.isBasicOperator(operator) || this.isNumericOperator(operator)) {
275
+ return this.formatComparison(path, this.COMPARISON_OPS[operator], value);
276
+ }
277
+ switch (operator) {
278
+ case "$in":
279
+ if (!Array.isArray(value) || value.length === 0) {
280
+ return "(HAS FIELD empty AND HAS NOT FIELD empty)";
281
+ }
282
+ return `${path} IN (${this.formatArray(value)})`;
283
+ case "$nin":
284
+ return `${path} NOT IN (${this.formatArray(value)})`;
285
+ case "$contains":
286
+ return `${path} CONTAINS ${this.formatValue(value)}`;
287
+ case "$regex":
288
+ return `${path} GLOB ${this.formatValue(value)}`;
289
+ case "$exists":
290
+ return value ? `HAS FIELD ${path}` : `HAS NOT FIELD ${path}`;
291
+ case "$and":
292
+ if (!Array.isArray(value) || value.length === 0) {
293
+ return "(HAS FIELD empty OR HAS NOT FIELD empty)";
294
+ }
295
+ return this.joinConditions(value, "AND");
296
+ case "$or":
297
+ if (!Array.isArray(value) || value.length === 0) {
298
+ return "(HAS FIELD empty AND HAS NOT FIELD empty)";
299
+ }
300
+ return this.joinConditions(value, "OR");
301
+ case "$not":
302
+ return this.formatNot(path, value);
303
+ case "$nor":
304
+ return this.formatNot("", { $or: value });
305
+ case "$all":
306
+ return this.translateOperator(
307
+ "$and",
308
+ value.map((item) => ({ [path]: { $contains: item } })),
309
+ ""
310
+ );
311
+ default:
312
+ throw new Error(`Unsupported operator: ${operator}`);
313
+ }
314
+ }
315
+ NEGATED_OPERATORS = {
316
+ $eq: "$ne",
317
+ $ne: "$eq",
318
+ $gt: "$lte",
319
+ $gte: "$lt",
320
+ $lt: "$gte",
321
+ $lte: "$gt",
322
+ $in: "$nin",
323
+ $nin: "$in",
324
+ $exists: "$exists"
325
+ // Special case - we'll flip the value
326
+ };
327
+ formatNot(path, value) {
328
+ if (typeof value !== "object") {
329
+ return `${path} != ${this.formatValue(value)}`;
330
+ }
331
+ if (!Object.keys(value).some((k) => k.startsWith("$"))) {
332
+ const [fieldName, fieldValue] = Object.entries(value)[0] ?? [];
333
+ if (typeof fieldValue === "object" && fieldValue !== null && Object.keys(fieldValue)[0]?.startsWith("$")) {
334
+ const [op2, val2] = Object.entries(fieldValue)[0] ?? [];
335
+ const negatedOp = this.NEGATED_OPERATORS[op2];
336
+ if (!negatedOp) throw new Error(`Unsupported operator in NOT: ${op2}`);
337
+ if (op2 === "$exists") {
338
+ return this.translateOperator(op2, !val2, fieldName ?? "");
339
+ }
340
+ return this.translateOperator(negatedOp, val2, fieldName ?? "");
341
+ }
342
+ return `${fieldName} != ${this.formatValue(fieldValue)}`;
343
+ }
344
+ const [op, val] = Object.entries(value)[0] ?? [];
345
+ if (op === "$lt") return `${path} >= ${this.formatValue(val)}`;
346
+ if (op === "$lte") return `${path} > ${this.formatValue(val)}`;
347
+ if (op === "$gt") return `${path} <= ${this.formatValue(val)}`;
348
+ if (op === "$gte") return `${path} < ${this.formatValue(val)}`;
349
+ if (op === "$ne") return `${path} = ${this.formatValue(val)}`;
350
+ if (op === "$eq") return `${path} != ${this.formatValue(val)}`;
351
+ if (op === "$contains") return `${path} NOT CONTAINS ${this.formatValue(val)}`;
352
+ if (op === "$regex") return `${path} NOT GLOB ${this.formatValue(val)}`;
353
+ if (op === "$in") return `${path} NOT IN (${this.formatArray(val)})`;
354
+ if (op === "$exists") return val ? `HAS NOT FIELD ${path}` : `HAS FIELD ${path}`;
355
+ if (op === "$and" || op === "$or") {
356
+ const newOp = op === "$and" ? "$or" : "$and";
357
+ const conditions = val.map((condition) => {
358
+ const [fieldName, fieldValue] = Object.entries(condition)[0] ?? [];
359
+ return { [fieldName]: { $not: fieldValue } };
360
+ });
361
+ return this.translateOperator(newOp, conditions, "");
362
+ }
363
+ if (op === "$nor") {
364
+ return this.translateOperator("$or", val, "");
365
+ }
366
+ return `${path} != ${this.formatValue(val)}`;
367
+ }
368
+ formatValue(value) {
369
+ if (value === null || value === void 0) {
370
+ throw new Error("Filtering for null/undefined values is not supported by Upstash Vector");
371
+ }
372
+ if (typeof value === "string") {
373
+ const hasSingleQuote = /'/g.test(value);
374
+ const hasDoubleQuote = /"/g.test(value);
375
+ if (hasSingleQuote && hasDoubleQuote) {
376
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
377
+ }
378
+ if (hasSingleQuote) {
379
+ return `"${value}"`;
380
+ }
381
+ return `'${value}'`;
382
+ }
383
+ if (typeof value === "number") {
384
+ if (Math.abs(value) < 1e-6 || Math.abs(value) > 1e6) {
385
+ return value.toFixed(20).replace(/\.?0+$/, "");
386
+ }
387
+ return value.toString();
388
+ }
389
+ return String(value);
390
+ }
391
+ formatArray(values) {
392
+ return values.map((value) => {
393
+ if (value === null || value === void 0) {
394
+ throw new Error("Filtering for null/undefined values is not supported by Upstash Vector");
395
+ }
396
+ return this.formatValue(value);
397
+ }).join(", ");
398
+ }
399
+ formatComparison(path, op, value) {
400
+ return `${path} ${op} ${this.formatValue(value)}`;
401
+ }
402
+ joinConditions(conditions, operator) {
403
+ const translated = Array.isArray(conditions) ? conditions.map((c) => this.translateNode(c)) : [this.translateNode(conditions)];
404
+ return `(${translated.join(` ${operator} `)})`;
405
+ }
406
+ };
407
+
408
+ // src/vector/index.ts
409
+ var UpstashVector = class extends vector.MastraVector {
410
+ client;
411
+ constructor({ url, token }) {
412
+ super();
413
+ this.client = new vector$1.Index({
414
+ url,
415
+ token
416
+ });
417
+ }
418
+ async upsert(...args) {
419
+ const params = this.normalizeArgs("upsert", args);
420
+ const { indexName, vectors, metadata, ids } = params;
421
+ const generatedIds = ids || vectors.map(() => crypto.randomUUID());
422
+ const points = vectors.map((vector, index) => ({
423
+ id: generatedIds[index],
424
+ vector,
425
+ metadata: metadata?.[index]
426
+ }));
427
+ await this.client.upsert(points, {
428
+ namespace: indexName
429
+ });
430
+ return generatedIds;
431
+ }
432
+ transformFilter(filter) {
433
+ const translator = new UpstashFilterTranslator();
434
+ return translator.translate(filter);
435
+ }
436
+ async createIndex(..._args) {
437
+ console.log("No need to call createIndex for Upstash");
438
+ }
439
+ async query(...args) {
440
+ const params = this.normalizeArgs("query", args);
441
+ const { indexName, queryVector, topK = 10, filter, includeVector = false } = params;
442
+ const ns = this.client.namespace(indexName);
443
+ const filterString = this.transformFilter(filter);
444
+ const results = await ns.query({
445
+ topK,
446
+ vector: queryVector,
447
+ includeVectors: includeVector,
448
+ includeMetadata: true,
449
+ ...filterString ? { filter: filterString } : {}
450
+ });
451
+ return (results || []).map((result) => ({
452
+ id: `${result.id}`,
453
+ score: result.score,
454
+ metadata: result.metadata,
455
+ ...includeVector && { vector: result.vector || [] }
456
+ }));
457
+ }
458
+ async listIndexes() {
459
+ const indexes = await this.client.listNamespaces();
460
+ return indexes.filter(Boolean);
461
+ }
462
+ async describeIndex(indexName) {
463
+ const info = await this.client.info();
464
+ return {
465
+ dimension: info.dimension,
466
+ count: info.namespaces?.[indexName]?.vectorCount || 0,
467
+ metric: info?.similarityFunction?.toLowerCase()
468
+ };
469
+ }
470
+ async deleteIndex(indexName) {
471
+ try {
472
+ await this.client.deleteNamespace(indexName);
473
+ } catch (error) {
474
+ console.error("Failed to delete namespace:", error);
475
+ }
476
+ }
477
+ };
478
+
479
+ exports.UpstashStore = UpstashStore;
480
+ exports.UpstashVector = UpstashVector;
@@ -0,0 +1,3 @@
1
+ export { UpstashConfig } from './_tsup-dts-rollup.cjs';
2
+ export { UpstashStore } from './_tsup-dts-rollup.cjs';
3
+ export { UpstashVector } from './_tsup-dts-rollup.cjs';
package/dist/index.js CHANGED
@@ -1,26 +1,18 @@
1
- import '@mastra/core/memory';
2
1
  import { MastraStorage } from '@mastra/core/storage';
3
- import '@mastra/core/workflows';
4
2
  import { Redis } from '@upstash/redis';
5
3
  import { MastraVector } from '@mastra/core/vector';
6
4
  import { Index } from '@upstash/vector';
7
- import { BaseFilterTranslator } from '@mastra/core/filter';
5
+ import { BaseFilterTranslator } from '@mastra/core/vector/filter';
8
6
 
9
7
  // src/storage/index.ts
10
8
  var UpstashStore = class extends MastraStorage {
11
- batchInsert({ tableName, records }) {
9
+ batchInsert(_input) {
12
10
  throw new Error("Method not implemented.");
13
11
  }
14
- getEvalsByAgentName(agentName, type) {
12
+ getEvalsByAgentName(_agentName, _type) {
15
13
  throw new Error("Method not implemented.");
16
14
  }
17
- getTraces({
18
- name,
19
- scope,
20
- page,
21
- perPage,
22
- attributes
23
- }) {
15
+ getTraces(_input) {
24
16
  throw new Error("Method not implemented.");
25
17
  }
26
18
  redis;
@@ -421,7 +413,9 @@ var UpstashVector = class extends MastraVector {
421
413
  token
422
414
  });
423
415
  }
424
- async upsert(indexName, vectors, metadata, ids) {
416
+ async upsert(...args) {
417
+ const params = this.normalizeArgs("upsert", args);
418
+ const { indexName, vectors, metadata, ids } = params;
425
419
  const generatedIds = ids || vectors.map(() => crypto.randomUUID());
426
420
  const points = vectors.map((vector, index) => ({
427
421
  id: generatedIds[index],
@@ -437,10 +431,12 @@ var UpstashVector = class extends MastraVector {
437
431
  const translator = new UpstashFilterTranslator();
438
432
  return translator.translate(filter);
439
433
  }
440
- async createIndex(_indexName, _dimension, _metric = "cosine") {
434
+ async createIndex(..._args) {
441
435
  console.log("No need to call createIndex for Upstash");
442
436
  }
443
- async query(indexName, queryVector, topK = 10, filter, includeVector = false) {
437
+ async query(...args) {
438
+ const params = this.normalizeArgs("query", args);
439
+ const { indexName, queryVector, topK = 10, filter, includeVector = false } = params;
444
440
  const ns = this.client.namespace(indexName);
445
441
  const filterString = this.transformFilter(filter);
446
442
  const results = await ns.query({
@@ -0,0 +1,6 @@
1
+ import { createConfig } from '@internal/lint/eslint';
2
+
3
+ const config = await createConfig();
4
+
5
+ /** @type {import("eslint").Linter.Config[]} */
6
+ export default [...config];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/upstash",
3
- "version": "0.1.6-alpha.0",
3
+ "version": "0.1.6-alpha.3",
4
4
  "description": "Upstash provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,6 +10,10 @@
10
10
  "import": {
11
11
  "types": "./dist/index.d.ts",
12
12
  "default": "./dist/index.js"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
13
17
  }
14
18
  },
15
19
  "./package.json": "./package.json"
@@ -17,20 +21,23 @@
17
21
  "dependencies": {
18
22
  "@upstash/redis": "^1.28.3",
19
23
  "@upstash/vector": "^1.1.7",
20
- "@mastra/core": "^0.4.3-alpha.0"
24
+ "@mastra/core": "^0.4.3-alpha.3"
21
25
  },
22
26
  "devDependencies": {
23
27
  "@microsoft/api-extractor": "^7.49.2",
24
28
  "@types/node": "^22.13.1",
25
29
  "tsup": "^8.0.1",
26
30
  "typescript": "^5.7.3",
27
- "vitest": "^3.0.4"
31
+ "vitest": "^3.0.4",
32
+ "eslint": "^9.20.1",
33
+ "@internal/lint": "0.0.0"
28
34
  },
29
35
  "scripts": {
30
36
  "pretest": "docker compose up -d",
31
37
  "test": "vitest run",
32
38
  "posttest": "docker compose down",
33
- "build": "tsup src/index.ts --format esm --experimental-dts --clean --treeshake",
39
+ "lint": "eslint .",
40
+ "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake",
34
41
  "build:watch": "pnpm build --watch"
35
42
  }
36
43
  }
@@ -1,12 +1,7 @@
1
- import { type StorageThreadType, type MessageType } from '@mastra/core/memory';
2
- import {
3
- MastraStorage,
4
- type TABLE_NAMES,
5
- type StorageColumn,
6
- type StorageGetMessagesArg,
7
- type EvalRow,
8
- } from '@mastra/core/storage';
9
- import { type WorkflowRunState } from '@mastra/core/workflows';
1
+ import type { StorageThreadType, MessageType } from '@mastra/core/memory';
2
+ import { MastraStorage } from '@mastra/core/storage';
3
+ import type { TABLE_NAMES, StorageColumn, StorageGetMessagesArg, EvalRow } from '@mastra/core/storage';
4
+ import type { WorkflowRunState } from '@mastra/core/workflows';
10
5
  import { Redis } from '@upstash/redis';
11
6
 
12
7
  export interface UpstashConfig {
@@ -15,19 +10,13 @@ export interface UpstashConfig {
15
10
  }
16
11
 
17
12
  export class UpstashStore extends MastraStorage {
18
- batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
13
+ batchInsert(_input: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
19
14
  throw new Error('Method not implemented.');
20
15
  }
21
- getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
16
+ getEvalsByAgentName(_agentName: string, _type?: 'test' | 'live'): Promise<EvalRow[]> {
22
17
  throw new Error('Method not implemented.');
23
18
  }
24
- getTraces({
25
- name,
26
- scope,
27
- page,
28
- perPage,
29
- attributes,
30
- }: {
19
+ getTraces(_input: {
31
20
  name?: string;
32
21
  scope?: string;
33
22
  page: number;
@@ -1,19 +1,27 @@
1
1
  import { MastraStorage } from '@mastra/core/storage';
2
- import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'vitest';
2
+ import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from 'vitest';
3
3
 
4
4
  import { UpstashStore } from './index';
5
5
 
6
+ // Increase timeout for all tests in this file to 30 seconds
7
+ vi.setConfig({ testTimeout: 60_000, hookTimeout: 60_000 });
8
+
6
9
  describe('UpstashStore', () => {
7
10
  let store: UpstashStore;
8
11
  const testTableName = 'test_table';
9
12
  const testTableName2 = 'test_table2';
10
13
 
11
14
  beforeAll(async () => {
15
+ console.log('Initializing UpstashStore...');
16
+
17
+ await new Promise(resolve => setTimeout(resolve, 5000));
12
18
  store = new UpstashStore({
13
19
  url: 'http://localhost:8079',
14
20
  token: 'test_token',
15
21
  });
22
+
16
23
  await store.init();
24
+ console.log('UpstashStore initialized');
17
25
  });
18
26
 
19
27
  afterAll(async () => {
@@ -1,4 +1,5 @@
1
- import { BaseFilterTranslator, type Filter, type FieldCondition, type OperatorSupport } from '@mastra/core/filter';
1
+ import { BaseFilterTranslator } from '@mastra/core/vector/filter';
2
+ import type { FieldCondition, OperatorSupport, VectorFilter } from '@mastra/core/vector/filter';
2
3
 
3
4
  export class UpstashFilterTranslator extends BaseFilterTranslator {
4
5
  protected override getSupportedOperators(): OperatorSupport {
@@ -10,13 +11,13 @@ export class UpstashFilterTranslator extends BaseFilterTranslator {
10
11
  };
11
12
  }
12
13
 
13
- translate(filter?: Filter): string | undefined {
14
+ translate(filter?: VectorFilter): string | undefined {
14
15
  if (this.isEmpty(filter)) return undefined;
15
- this.validateFilter(filter as Filter);
16
+ this.validateFilter(filter);
16
17
  return this.translateNode(filter);
17
18
  }
18
19
 
19
- private translateNode(node: Filter | FieldCondition, path: string = ''): string {
20
+ private translateNode(node: VectorFilter | FieldCondition, path: string = ''): string {
20
21
  if (this.isRegex(node)) {
21
22
  throw new Error('Direct regex pattern format is not supported in Upstash');
22
23
  }