@mastra/upstash 0.12.1 → 0.12.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/dist/index.js CHANGED
@@ -1,157 +1,49 @@
1
- import { MessageList } from '@mastra/core/agent';
2
- import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
3
- import { MastraStorage, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_TRACES, TABLE_THREADS, TABLE_RESOURCES } from '@mastra/core/storage';
1
+ import { MastraStorage, StoreOperations, TracesStorage, TABLE_TRACES, ScoresStorage, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MemoryStorage, TABLE_THREADS, resolveMessageLimit, TABLE_RESOURCES, LegacyEvalsStorage, TABLE_EVALS, TABLE_MESSAGES, serializeDate } from '@mastra/core/storage';
4
2
  import { Redis } from '@upstash/redis';
3
+ import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
4
+ import { MessageList } from '@mastra/core/agent';
5
5
  import { MastraVector } from '@mastra/core/vector';
6
6
  import { Index } from '@upstash/vector';
7
7
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
8
8
 
9
9
  // src/storage/index.ts
10
- var UpstashStore = class extends MastraStorage {
11
- redis;
12
- constructor(config) {
13
- super({ name: "Upstash" });
14
- this.redis = new Redis({
15
- url: config.url,
16
- token: config.token
17
- });
18
- }
19
- get supports() {
20
- return {
21
- selectByIncludeResourceScope: true,
22
- resourceWorkingMemory: true
23
- };
24
- }
25
- transformEvalRecord(record) {
26
- let result = record.result;
27
- if (typeof result === "string") {
28
- try {
29
- result = JSON.parse(result);
30
- } catch {
31
- console.warn("Failed to parse result JSON:");
32
- }
33
- }
34
- let testInfo = record.test_info;
35
- if (typeof testInfo === "string") {
36
- try {
37
- testInfo = JSON.parse(testInfo);
38
- } catch {
39
- console.warn("Failed to parse test_info JSON:");
40
- }
41
- }
42
- return {
43
- agentName: record.agent_name,
44
- input: record.input,
45
- output: record.output,
46
- result,
47
- metricName: record.metric_name,
48
- instructions: record.instructions,
49
- testInfo,
50
- globalRunId: record.global_run_id,
51
- runId: record.run_id,
52
- createdAt: typeof record.created_at === "string" ? record.created_at : record.created_at instanceof Date ? record.created_at.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
53
- };
54
- }
55
- parseJSON(value) {
56
- if (typeof value === "string") {
57
- try {
58
- return JSON.parse(value);
59
- } catch {
60
- return value;
61
- }
10
+ function transformEvalRecord(record) {
11
+ let result = record.result;
12
+ if (typeof result === "string") {
13
+ try {
14
+ result = JSON.parse(result);
15
+ } catch {
16
+ console.warn("Failed to parse result JSON:");
62
17
  }
63
- return value;
64
- }
65
- getKey(tableName, keys) {
66
- const keyParts = Object.entries(keys).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`);
67
- return `${tableName}:${keyParts.join(":")}`;
68
- }
69
- /**
70
- * Scans for keys matching the given pattern using SCAN and returns them as an array.
71
- * @param pattern Redis key pattern, e.g. "table:*"
72
- * @param batchSize Number of keys to scan per batch (default: 1000)
73
- */
74
- async scanKeys(pattern, batchSize = 1e4) {
75
- let cursor = "0";
76
- let keys = [];
77
- do {
78
- const [nextCursor, batch] = await this.redis.scan(cursor, {
79
- match: pattern,
80
- count: batchSize
81
- });
82
- keys.push(...batch);
83
- cursor = nextCursor;
84
- } while (cursor !== "0");
85
- return keys;
86
- }
87
- /**
88
- * Deletes all keys matching the given pattern using SCAN and DEL in batches.
89
- * @param pattern Redis key pattern, e.g. "table:*"
90
- * @param batchSize Number of keys to delete per batch (default: 1000)
91
- */
92
- async scanAndDelete(pattern, batchSize = 1e4) {
93
- let cursor = "0";
94
- let totalDeleted = 0;
95
- do {
96
- const [nextCursor, keys] = await this.redis.scan(cursor, {
97
- match: pattern,
98
- count: batchSize
99
- });
100
- if (keys.length > 0) {
101
- await this.redis.del(...keys);
102
- totalDeleted += keys.length;
103
- }
104
- cursor = nextCursor;
105
- } while (cursor !== "0");
106
- return totalDeleted;
107
- }
108
- getMessageKey(threadId, messageId) {
109
- const key = this.getKey(TABLE_MESSAGES, { threadId, id: messageId });
110
- return key;
111
18
  }
112
- getThreadMessagesKey(threadId) {
113
- return `thread:${threadId}:messages`;
114
- }
115
- parseWorkflowRun(row) {
116
- let parsedSnapshot = row.snapshot;
117
- if (typeof parsedSnapshot === "string") {
118
- try {
119
- parsedSnapshot = JSON.parse(row.snapshot);
120
- } catch (e) {
121
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
122
- }
19
+ let testInfo = record.test_info;
20
+ if (typeof testInfo === "string") {
21
+ try {
22
+ testInfo = JSON.parse(testInfo);
23
+ } catch {
24
+ console.warn("Failed to parse test_info JSON:");
123
25
  }
124
- return {
125
- workflowName: row.workflow_name,
126
- runId: row.run_id,
127
- snapshot: parsedSnapshot,
128
- createdAt: this.ensureDate(row.createdAt),
129
- updatedAt: this.ensureDate(row.updatedAt),
130
- resourceId: row.resourceId
131
- };
132
26
  }
133
- processRecord(tableName, record) {
134
- let key;
135
- if (tableName === TABLE_MESSAGES) {
136
- key = this.getKey(tableName, { threadId: record.threadId, id: record.id });
137
- } else if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
138
- key = this.getKey(tableName, {
139
- namespace: record.namespace || "workflows",
140
- workflow_name: record.workflow_name,
141
- run_id: record.run_id,
142
- ...record.resourceId ? { resourceId: record.resourceId } : {}
143
- });
144
- } else if (tableName === TABLE_EVALS) {
145
- key = this.getKey(tableName, { id: record.run_id });
146
- } else {
147
- key = this.getKey(tableName, { id: record.id });
148
- }
149
- const processedRecord = {
150
- ...record,
151
- createdAt: this.serializeDate(record.createdAt),
152
- updatedAt: this.serializeDate(record.updatedAt)
153
- };
154
- return { key, processedRecord };
27
+ return {
28
+ agentName: record.agent_name,
29
+ input: record.input,
30
+ output: record.output,
31
+ result,
32
+ metricName: record.metric_name,
33
+ instructions: record.instructions,
34
+ testInfo,
35
+ globalRunId: record.global_run_id,
36
+ runId: record.run_id,
37
+ createdAt: typeof record.created_at === "string" ? record.created_at : record.created_at instanceof Date ? record.created_at.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
38
+ };
39
+ }
40
+ var StoreLegacyEvalsUpstash = class extends LegacyEvalsStorage {
41
+ client;
42
+ operations;
43
+ constructor({ client, operations }) {
44
+ super();
45
+ this.client = client;
46
+ this.operations = operations;
155
47
  }
156
48
  /**
157
49
  * @deprecated Use getEvals instead
@@ -159,11 +51,11 @@ var UpstashStore = class extends MastraStorage {
159
51
  async getEvalsByAgentName(agentName, type) {
160
52
  try {
161
53
  const pattern = `${TABLE_EVALS}:*`;
162
- const keys = await this.scanKeys(pattern);
54
+ const keys = await this.operations.scanKeys(pattern);
163
55
  if (keys.length === 0) {
164
56
  return [];
165
57
  }
166
- const pipeline = this.redis.pipeline();
58
+ const pipeline = this.client.pipeline();
167
59
  keys.forEach((key) => pipeline.get(key));
168
60
  const results = await pipeline.exec();
169
61
  const nonNullRecords = results.filter(
@@ -197,7 +89,7 @@ var UpstashStore = class extends MastraStorage {
197
89
  }
198
90
  });
199
91
  }
200
- return filteredEvals.map((record) => this.transformEvalRecord(record));
92
+ return filteredEvals.map((record) => transformEvalRecord(record));
201
93
  } catch (error) {
202
94
  const mastraError = new MastraError(
203
95
  {
@@ -214,148 +106,198 @@ var UpstashStore = class extends MastraStorage {
214
106
  }
215
107
  }
216
108
  /**
217
- * @deprecated use getTracesPaginated instead
109
+ * Get all evaluations with pagination and total count
110
+ * @param options Pagination and filtering options
111
+ * @returns Object with evals array and total count
218
112
  */
219
- async getTraces(args) {
220
- if (args.fromDate || args.toDate) {
221
- args.dateRange = {
222
- start: args.fromDate,
223
- end: args.toDate
224
- };
225
- }
226
- try {
227
- const { traces } = await this.getTracesPaginated(args);
228
- return traces;
229
- } catch (error) {
230
- throw new MastraError(
231
- {
232
- id: "STORAGE_UPSTASH_STORAGE_GET_TRACES_FAILED",
233
- domain: ErrorDomain.STORAGE,
234
- category: ErrorCategory.THIRD_PARTY
235
- },
236
- error
237
- );
238
- }
239
- }
240
- async getTracesPaginated(args) {
241
- const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
242
- const fromDate = dateRange?.start;
243
- const toDate = dateRange?.end;
113
+ async getEvals(options) {
244
114
  try {
245
- const pattern = `${TABLE_TRACES}:*`;
246
- const keys = await this.scanKeys(pattern);
115
+ const { agentName, type, page = 0, perPage = 100, dateRange } = options || {};
116
+ const fromDate = dateRange?.start;
117
+ const toDate = dateRange?.end;
118
+ const pattern = `${TABLE_EVALS}:*`;
119
+ const keys = await this.operations.scanKeys(pattern);
247
120
  if (keys.length === 0) {
248
121
  return {
249
- traces: [],
122
+ evals: [],
250
123
  total: 0,
251
124
  page,
252
- perPage: perPage || 100,
125
+ perPage,
253
126
  hasMore: false
254
127
  };
255
128
  }
256
- const pipeline = this.redis.pipeline();
129
+ const pipeline = this.client.pipeline();
257
130
  keys.forEach((key) => pipeline.get(key));
258
131
  const results = await pipeline.exec();
259
- let filteredTraces = results.filter(
260
- (record) => record !== null && typeof record === "object"
261
- );
262
- if (name) {
263
- filteredTraces = filteredTraces.filter((record) => record.name?.toLowerCase().startsWith(name.toLowerCase()));
264
- }
265
- if (scope) {
266
- filteredTraces = filteredTraces.filter((record) => record.scope === scope);
132
+ let filteredEvals = results.map((result) => result).filter((record) => record !== null && typeof record === "object");
133
+ if (agentName) {
134
+ filteredEvals = filteredEvals.filter((record) => record.agent_name === agentName);
267
135
  }
268
- if (attributes) {
269
- filteredTraces = filteredTraces.filter((record) => {
270
- const recordAttributes = record.attributes;
271
- if (!recordAttributes) return false;
272
- const parsedAttributes = typeof recordAttributes === "string" ? JSON.parse(recordAttributes) : recordAttributes;
273
- return Object.entries(attributes).every(([key, value]) => parsedAttributes[key] === value);
136
+ if (type === "test") {
137
+ filteredEvals = filteredEvals.filter((record) => {
138
+ if (!record.test_info) return false;
139
+ try {
140
+ if (typeof record.test_info === "string") {
141
+ const parsedTestInfo = JSON.parse(record.test_info);
142
+ return parsedTestInfo && typeof parsedTestInfo === "object" && "testPath" in parsedTestInfo;
143
+ }
144
+ return typeof record.test_info === "object" && "testPath" in record.test_info;
145
+ } catch {
146
+ return false;
147
+ }
148
+ });
149
+ } else if (type === "live") {
150
+ filteredEvals = filteredEvals.filter((record) => {
151
+ if (!record.test_info) return true;
152
+ try {
153
+ if (typeof record.test_info === "string") {
154
+ const parsedTestInfo = JSON.parse(record.test_info);
155
+ return !(parsedTestInfo && typeof parsedTestInfo === "object" && "testPath" in parsedTestInfo);
156
+ }
157
+ return !(typeof record.test_info === "object" && "testPath" in record.test_info);
158
+ } catch {
159
+ return true;
160
+ }
274
161
  });
275
- }
276
- if (filters) {
277
- filteredTraces = filteredTraces.filter(
278
- (record) => Object.entries(filters).every(([key, value]) => record[key] === value)
279
- );
280
162
  }
281
163
  if (fromDate) {
282
- filteredTraces = filteredTraces.filter(
283
- (record) => new Date(record.createdAt).getTime() >= new Date(fromDate).getTime()
284
- );
164
+ filteredEvals = filteredEvals.filter((record) => {
165
+ const createdAt = new Date(record.created_at || record.createdAt || 0);
166
+ return createdAt.getTime() >= fromDate.getTime();
167
+ });
285
168
  }
286
169
  if (toDate) {
287
- filteredTraces = filteredTraces.filter(
288
- (record) => new Date(record.createdAt).getTime() <= new Date(toDate).getTime()
289
- );
170
+ filteredEvals = filteredEvals.filter((record) => {
171
+ const createdAt = new Date(record.created_at || record.createdAt || 0);
172
+ return createdAt.getTime() <= toDate.getTime();
173
+ });
290
174
  }
291
- filteredTraces.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
292
- const transformedTraces = filteredTraces.map((record) => ({
293
- id: record.id,
294
- parentSpanId: record.parentSpanId,
295
- traceId: record.traceId,
296
- name: record.name,
297
- scope: record.scope,
298
- kind: record.kind,
299
- status: this.parseJSON(record.status),
300
- events: this.parseJSON(record.events),
301
- links: this.parseJSON(record.links),
302
- attributes: this.parseJSON(record.attributes),
303
- startTime: record.startTime,
304
- endTime: record.endTime,
305
- other: this.parseJSON(record.other),
306
- createdAt: this.ensureDate(record.createdAt)
307
- }));
308
- const total = transformedTraces.length;
309
- const resolvedPerPage = perPage || 100;
310
- const start = page * resolvedPerPage;
311
- const end = start + resolvedPerPage;
312
- const paginatedTraces = transformedTraces.slice(start, end);
175
+ filteredEvals.sort((a, b) => {
176
+ const dateA = new Date(a.created_at || a.createdAt || 0).getTime();
177
+ const dateB = new Date(b.created_at || b.createdAt || 0).getTime();
178
+ return dateB - dateA;
179
+ });
180
+ const total = filteredEvals.length;
181
+ const start = page * perPage;
182
+ const end = start + perPage;
183
+ const paginatedEvals = filteredEvals.slice(start, end);
313
184
  const hasMore = end < total;
185
+ const evals = paginatedEvals.map((record) => transformEvalRecord(record));
314
186
  return {
315
- traces: paginatedTraces,
187
+ evals,
316
188
  total,
317
189
  page,
318
- perPage: resolvedPerPage,
190
+ perPage,
319
191
  hasMore
320
192
  };
321
193
  } catch (error) {
194
+ const { page = 0, perPage = 100 } = options || {};
322
195
  const mastraError = new MastraError(
323
196
  {
324
- id: "STORAGE_UPSTASH_STORAGE_GET_TRACES_PAGINATED_FAILED",
197
+ id: "STORAGE_UPSTASH_STORAGE_GET_EVALS_FAILED",
325
198
  domain: ErrorDomain.STORAGE,
326
199
  category: ErrorCategory.THIRD_PARTY,
327
200
  details: {
328
- name: args.name || "",
329
- scope: args.scope || ""
201
+ page,
202
+ perPage
330
203
  }
331
204
  },
332
205
  error
333
206
  );
334
- this.logger?.trackException(mastraError);
335
207
  this.logger.error(mastraError.toString());
208
+ this.logger?.trackException(mastraError);
336
209
  return {
337
- traces: [],
210
+ evals: [],
338
211
  total: 0,
339
212
  page,
340
- perPage: perPage || 100,
213
+ perPage,
341
214
  hasMore: false
342
215
  };
343
216
  }
344
217
  }
345
- async createTable({
346
- tableName,
347
- schema
348
- }) {
218
+ };
219
+ function ensureDate(value) {
220
+ if (!value) return null;
221
+ if (value instanceof Date) return value;
222
+ if (typeof value === "string") return new Date(value);
223
+ if (typeof value === "number") return new Date(value);
224
+ return null;
225
+ }
226
+ function parseJSON(value) {
227
+ if (typeof value === "string") {
228
+ try {
229
+ return JSON.parse(value);
230
+ } catch {
231
+ return value;
232
+ }
233
+ }
234
+ return value;
235
+ }
236
+ function getKey(tableName, keys) {
237
+ const keyParts = Object.entries(keys).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`);
238
+ return `${tableName}:${keyParts.join(":")}`;
239
+ }
240
+ function processRecord(tableName, record) {
241
+ let key;
242
+ if (tableName === TABLE_MESSAGES) {
243
+ key = getKey(tableName, { threadId: record.threadId, id: record.id });
244
+ } else if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
245
+ key = getKey(tableName, {
246
+ namespace: record.namespace || "workflows",
247
+ workflow_name: record.workflow_name,
248
+ run_id: record.run_id,
249
+ ...record.resourceId ? { resourceId: record.resourceId } : {}
250
+ });
251
+ } else if (tableName === TABLE_EVALS) {
252
+ key = getKey(tableName, { id: record.run_id });
253
+ } else {
254
+ key = getKey(tableName, { id: record.id });
255
+ }
256
+ const processedRecord = {
257
+ ...record,
258
+ createdAt: serializeDate(record.createdAt),
259
+ updatedAt: serializeDate(record.updatedAt)
260
+ };
261
+ return { key, processedRecord };
262
+ }
263
+
264
+ // src/storage/domains/memory/index.ts
265
+ function getThreadMessagesKey(threadId) {
266
+ return `thread:${threadId}:messages`;
267
+ }
268
+ function getMessageKey(threadId, messageId) {
269
+ const key = getKey(TABLE_MESSAGES, { threadId, id: messageId });
270
+ return key;
271
+ }
272
+ var StoreMemoryUpstash = class extends MemoryStorage {
273
+ client;
274
+ operations;
275
+ constructor({ client, operations }) {
276
+ super();
277
+ this.client = client;
278
+ this.operations = operations;
279
+ }
280
+ async getThreadById({ threadId }) {
349
281
  try {
350
- await this.redis.set(`schema:${tableName}`, schema);
282
+ const thread = await this.operations.load({
283
+ tableName: TABLE_THREADS,
284
+ keys: { id: threadId }
285
+ });
286
+ if (!thread) return null;
287
+ return {
288
+ ...thread,
289
+ createdAt: ensureDate(thread.createdAt),
290
+ updatedAt: ensureDate(thread.updatedAt),
291
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
292
+ };
351
293
  } catch (error) {
352
294
  throw new MastraError(
353
295
  {
354
- id: "STORAGE_UPSTASH_STORAGE_CREATE_TABLE_FAILED",
296
+ id: "STORAGE_UPSTASH_STORAGE_GET_THREAD_BY_ID_FAILED",
355
297
  domain: ErrorDomain.STORAGE,
356
298
  category: ErrorCategory.THIRD_PARTY,
357
299
  details: {
358
- tableName
300
+ threadId
359
301
  }
360
302
  },
361
303
  error
@@ -363,135 +305,17 @@ var UpstashStore = class extends MastraStorage {
363
305
  }
364
306
  }
365
307
  /**
366
- * No-op: This backend is schemaless and does not require schema changes.
367
- * @param tableName Name of the table
368
- * @param schema Schema of the table
369
- * @param ifNotExists Array of column names to add if they don't exist
308
+ * @deprecated use getThreadsByResourceIdPaginated instead
370
309
  */
371
- async alterTable(_args) {
372
- }
373
- async clearTable({ tableName }) {
374
- const pattern = `${tableName}:*`;
375
- try {
376
- await this.scanAndDelete(pattern);
377
- } catch (error) {
378
- throw new MastraError(
379
- {
380
- id: "STORAGE_UPSTASH_STORAGE_CLEAR_TABLE_FAILED",
381
- domain: ErrorDomain.STORAGE,
382
- category: ErrorCategory.THIRD_PARTY,
383
- details: {
384
- tableName
385
- }
386
- },
387
- error
388
- );
389
- }
390
- }
391
- async insert({ tableName, record }) {
392
- const { key, processedRecord } = this.processRecord(tableName, record);
393
- try {
394
- await this.redis.set(key, processedRecord);
395
- } catch (error) {
396
- throw new MastraError(
397
- {
398
- id: "STORAGE_UPSTASH_STORAGE_INSERT_FAILED",
399
- domain: ErrorDomain.STORAGE,
400
- category: ErrorCategory.THIRD_PARTY,
401
- details: {
402
- tableName
403
- }
404
- },
405
- error
406
- );
407
- }
408
- }
409
- async batchInsert(input) {
410
- const { tableName, records } = input;
411
- if (!records.length) return;
412
- const batchSize = 1e3;
413
- try {
414
- for (let i = 0; i < records.length; i += batchSize) {
415
- const batch = records.slice(i, i + batchSize);
416
- const pipeline = this.redis.pipeline();
417
- for (const record of batch) {
418
- const { key, processedRecord } = this.processRecord(tableName, record);
419
- pipeline.set(key, processedRecord);
420
- }
421
- await pipeline.exec();
422
- }
423
- } catch (error) {
424
- throw new MastraError(
425
- {
426
- id: "STORAGE_UPSTASH_STORAGE_BATCH_INSERT_FAILED",
427
- domain: ErrorDomain.STORAGE,
428
- category: ErrorCategory.THIRD_PARTY,
429
- details: {
430
- tableName
431
- }
432
- },
433
- error
434
- );
435
- }
436
- }
437
- async load({ tableName, keys }) {
438
- const key = this.getKey(tableName, keys);
439
- try {
440
- const data = await this.redis.get(key);
441
- return data || null;
442
- } catch (error) {
443
- throw new MastraError(
444
- {
445
- id: "STORAGE_UPSTASH_STORAGE_LOAD_FAILED",
446
- domain: ErrorDomain.STORAGE,
447
- category: ErrorCategory.THIRD_PARTY,
448
- details: {
449
- tableName
450
- }
451
- },
452
- error
453
- );
454
- }
455
- }
456
- async getThreadById({ threadId }) {
457
- try {
458
- const thread = await this.load({
459
- tableName: TABLE_THREADS,
460
- keys: { id: threadId }
461
- });
462
- if (!thread) return null;
463
- return {
464
- ...thread,
465
- createdAt: this.ensureDate(thread.createdAt),
466
- updatedAt: this.ensureDate(thread.updatedAt),
467
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
468
- };
469
- } catch (error) {
470
- throw new MastraError(
471
- {
472
- id: "STORAGE_UPSTASH_STORAGE_GET_THREAD_BY_ID_FAILED",
473
- domain: ErrorDomain.STORAGE,
474
- category: ErrorCategory.THIRD_PARTY,
475
- details: {
476
- threadId
477
- }
478
- },
479
- error
480
- );
481
- }
482
- }
483
- /**
484
- * @deprecated use getThreadsByResourceIdPaginated instead
485
- */
486
- async getThreadsByResourceId({ resourceId }) {
310
+ async getThreadsByResourceId({ resourceId }) {
487
311
  try {
488
312
  const pattern = `${TABLE_THREADS}:*`;
489
- const keys = await this.scanKeys(pattern);
313
+ const keys = await this.operations.scanKeys(pattern);
490
314
  if (keys.length === 0) {
491
315
  return [];
492
316
  }
493
317
  const allThreads = [];
494
- const pipeline = this.redis.pipeline();
318
+ const pipeline = this.client.pipeline();
495
319
  keys.forEach((key) => pipeline.get(key));
496
320
  const results = await pipeline.exec();
497
321
  for (let i = 0; i < results.length; i++) {
@@ -499,8 +323,8 @@ var UpstashStore = class extends MastraStorage {
499
323
  if (thread && thread.resourceId === resourceId) {
500
324
  allThreads.push({
501
325
  ...thread,
502
- createdAt: this.ensureDate(thread.createdAt),
503
- updatedAt: this.ensureDate(thread.updatedAt),
326
+ createdAt: ensureDate(thread.createdAt),
327
+ updatedAt: ensureDate(thread.updatedAt),
504
328
  metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
505
329
  });
506
330
  }
@@ -567,7 +391,7 @@ var UpstashStore = class extends MastraStorage {
567
391
  }
568
392
  async saveThread({ thread }) {
569
393
  try {
570
- await this.insert({
394
+ await this.operations.insert({
571
395
  tableName: TABLE_THREADS,
572
396
  record: thread
573
397
  });
@@ -632,20 +456,20 @@ var UpstashStore = class extends MastraStorage {
632
456
  }
633
457
  }
634
458
  async deleteThread({ threadId }) {
635
- const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
636
- const threadMessagesKey = this.getThreadMessagesKey(threadId);
459
+ const threadKey = getKey(TABLE_THREADS, { id: threadId });
460
+ const threadMessagesKey = getThreadMessagesKey(threadId);
637
461
  try {
638
- const messageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
639
- const pipeline = this.redis.pipeline();
462
+ const messageIds = await this.client.zrange(threadMessagesKey, 0, -1);
463
+ const pipeline = this.client.pipeline();
640
464
  pipeline.del(threadKey);
641
465
  pipeline.del(threadMessagesKey);
642
466
  for (let i = 0; i < messageIds.length; i++) {
643
467
  const messageId = messageIds[i];
644
- const messageKey = this.getMessageKey(threadId, messageId);
468
+ const messageKey = getMessageKey(threadId, messageId);
645
469
  pipeline.del(messageKey);
646
470
  }
647
471
  await pipeline.exec();
648
- await this.scanAndDelete(this.getMessageKey(threadId, "*"));
472
+ await this.operations.scanAndDelete(getMessageKey(threadId, "*"));
649
473
  } catch (error) {
650
474
  throw new MastraError(
651
475
  {
@@ -682,40 +506,52 @@ var UpstashStore = class extends MastraStorage {
682
506
  error
683
507
  );
684
508
  }
685
- const messagesWithIndex = messages.map((message, index) => ({
686
- ...message,
687
- _index: index
688
- }));
689
- const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
690
- const existingThread = await this.redis.get(threadKey);
509
+ const messagesWithIndex = messages.map((message, index) => {
510
+ if (!message.threadId) {
511
+ throw new Error(
512
+ `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
513
+ );
514
+ }
515
+ if (!message.resourceId) {
516
+ throw new Error(
517
+ `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
518
+ );
519
+ }
520
+ return {
521
+ ...message,
522
+ _index: index
523
+ };
524
+ });
525
+ const threadKey = getKey(TABLE_THREADS, { id: threadId });
526
+ const existingThread = await this.client.get(threadKey);
691
527
  try {
692
528
  const batchSize = 1e3;
693
529
  for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
694
530
  const batch = messagesWithIndex.slice(i, i + batchSize);
695
- const pipeline = this.redis.pipeline();
531
+ const pipeline = this.client.pipeline();
696
532
  for (const message of batch) {
697
- const key = this.getMessageKey(message.threadId, message.id);
533
+ const key = getMessageKey(message.threadId, message.id);
698
534
  const createdAtScore = new Date(message.createdAt).getTime();
699
535
  const score = message._index !== void 0 ? message._index : createdAtScore;
700
- const existingKeyPattern = this.getMessageKey("*", message.id);
701
- const keys = await this.scanKeys(existingKeyPattern);
536
+ const existingKeyPattern = getMessageKey("*", message.id);
537
+ const keys = await this.operations.scanKeys(existingKeyPattern);
702
538
  if (keys.length > 0) {
703
- const pipeline2 = this.redis.pipeline();
539
+ const pipeline2 = this.client.pipeline();
704
540
  keys.forEach((key2) => pipeline2.get(key2));
705
541
  const results = await pipeline2.exec();
706
542
  const existingMessages = results.filter(
707
543
  (msg) => msg !== null
708
544
  );
709
545
  for (const existingMessage of existingMessages) {
710
- const existingMessageKey = this.getMessageKey(existingMessage.threadId, existingMessage.id);
546
+ const existingMessageKey = getMessageKey(existingMessage.threadId, existingMessage.id);
711
547
  if (existingMessage && existingMessage.threadId !== message.threadId) {
712
548
  pipeline.del(existingMessageKey);
713
- pipeline.zrem(this.getThreadMessagesKey(existingMessage.threadId), existingMessage.id);
549
+ pipeline.zrem(getThreadMessagesKey(existingMessage.threadId), existingMessage.id);
714
550
  }
715
551
  }
716
552
  }
717
553
  pipeline.set(key, message);
718
- pipeline.zadd(this.getThreadMessagesKey(message.threadId), {
554
+ pipeline.zadd(getThreadMessagesKey(message.threadId), {
719
555
  score,
720
556
  member: message.id
721
557
  });
@@ -725,7 +561,7 @@ var UpstashStore = class extends MastraStorage {
725
561
  ...existingThread,
726
562
  updatedAt: /* @__PURE__ */ new Date()
727
563
  };
728
- pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
564
+ pipeline.set(threadKey, processRecord(TABLE_THREADS, updatedThread).processedRecord);
729
565
  }
730
566
  await pipeline.exec();
731
567
  }
@@ -754,29 +590,29 @@ var UpstashStore = class extends MastraStorage {
754
590
  messageIds.add(item.id);
755
591
  const itemThreadId = item.threadId || threadId;
756
592
  messageIdToThreadIds[item.id] = itemThreadId;
757
- const itemThreadMessagesKey = this.getThreadMessagesKey(itemThreadId);
758
- const rank = await this.redis.zrank(itemThreadMessagesKey, item.id);
593
+ const itemThreadMessagesKey = getThreadMessagesKey(itemThreadId);
594
+ const rank = await this.client.zrank(itemThreadMessagesKey, item.id);
759
595
  if (rank === null) continue;
760
596
  if (item.withPreviousMessages) {
761
597
  const start = Math.max(0, rank - item.withPreviousMessages);
762
- const prevIds = rank === 0 ? [] : await this.redis.zrange(itemThreadMessagesKey, start, rank - 1);
598
+ const prevIds = rank === 0 ? [] : await this.client.zrange(itemThreadMessagesKey, start, rank - 1);
763
599
  prevIds.forEach((id) => {
764
600
  messageIds.add(id);
765
601
  messageIdToThreadIds[id] = itemThreadId;
766
602
  });
767
603
  }
768
604
  if (item.withNextMessages) {
769
- const nextIds = await this.redis.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
605
+ const nextIds = await this.client.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
770
606
  nextIds.forEach((id) => {
771
607
  messageIds.add(id);
772
608
  messageIdToThreadIds[id] = itemThreadId;
773
609
  });
774
610
  }
775
611
  }
776
- const pipeline = this.redis.pipeline();
612
+ const pipeline = this.client.pipeline();
777
613
  Array.from(messageIds).forEach((id) => {
778
614
  const tId = messageIdToThreadIds[id] || threadId;
779
- pipeline.get(this.getMessageKey(tId, id));
615
+ pipeline.get(getMessageKey(tId, id));
780
616
  });
781
617
  const results = await pipeline.exec();
782
618
  return results.filter((result) => result !== null);
@@ -788,23 +624,23 @@ var UpstashStore = class extends MastraStorage {
788
624
  selectBy,
789
625
  format
790
626
  }) {
791
- const threadMessagesKey = this.getThreadMessagesKey(threadId);
627
+ const threadMessagesKey = getThreadMessagesKey(threadId);
792
628
  try {
793
- const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
794
- const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
629
+ const allMessageIds = await this.client.zrange(threadMessagesKey, 0, -1);
630
+ const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
795
631
  const messageIds = /* @__PURE__ */ new Set();
796
632
  const messageIdToThreadIds = {};
797
633
  if (limit === 0 && !selectBy?.include) {
798
634
  return [];
799
635
  }
800
636
  if (limit === Number.MAX_SAFE_INTEGER) {
801
- const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
637
+ const allIds = await this.client.zrange(threadMessagesKey, 0, -1);
802
638
  allIds.forEach((id) => {
803
639
  messageIds.add(id);
804
640
  messageIdToThreadIds[id] = threadId;
805
641
  });
806
642
  } else if (limit > 0) {
807
- const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
643
+ const latestIds = await this.client.zrange(threadMessagesKey, -limit, -1);
808
644
  latestIds.forEach((id) => {
809
645
  messageIds.add(id);
810
646
  messageIdToThreadIds[id] = threadId;
@@ -816,9 +652,7 @@ var UpstashStore = class extends MastraStorage {
816
652
  ...(await Promise.all(
817
653
  Array.from(messageIds).map(async (id) => {
818
654
  const tId = messageIdToThreadIds[id] || threadId;
819
- const byThreadId = await this.redis.get(
820
- this.getMessageKey(tId, id)
821
- );
655
+ const byThreadId = await this.client.get(getMessageKey(tId, id));
822
656
  if (byThreadId) return byThreadId;
823
657
  return null;
824
658
  })
@@ -838,10 +672,14 @@ var UpstashStore = class extends MastraStorage {
838
672
  if (format === "v2") {
839
673
  return prepared.map((msg) => ({
840
674
  ...msg,
675
+ createdAt: new Date(msg.createdAt),
841
676
  content: msg.content || { format: 2, parts: [{ type: "text", text: "" }] }
842
677
  }));
843
678
  }
844
- return prepared;
679
+ return prepared.map((msg) => ({
680
+ ...msg,
681
+ createdAt: new Date(msg.createdAt)
682
+ }));
845
683
  } catch (error) {
846
684
  throw new MastraError(
847
685
  {
@@ -861,12 +699,16 @@ var UpstashStore = class extends MastraStorage {
861
699
  const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
862
700
  const fromDate = dateRange?.start;
863
701
  const toDate = dateRange?.end;
864
- const threadMessagesKey = this.getThreadMessagesKey(threadId);
702
+ const threadMessagesKey = getThreadMessagesKey(threadId);
865
703
  const messages = [];
866
704
  try {
867
705
  const includedMessages = await this._getIncludedMessages(threadId, selectBy);
868
706
  messages.push(...includedMessages);
869
- const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
707
+ const allMessageIds = await this.client.zrange(
708
+ threadMessagesKey,
709
+ args?.selectBy?.last ? -args.selectBy.last : 0,
710
+ -1
711
+ );
870
712
  if (allMessageIds.length === 0) {
871
713
  return {
872
714
  messages: [],
@@ -876,8 +718,8 @@ var UpstashStore = class extends MastraStorage {
876
718
  hasMore: false
877
719
  };
878
720
  }
879
- const pipeline = this.redis.pipeline();
880
- allMessageIds.forEach((id) => pipeline.get(this.getMessageKey(threadId, id)));
721
+ const pipeline = this.client.pipeline();
722
+ allMessageIds.forEach((id) => pipeline.get(getMessageKey(threadId, id)));
881
723
  const results = await pipeline.exec();
882
724
  let messagesData = results.filter((msg) => msg !== null);
883
725
  if (fromDate) {
@@ -925,206 +767,795 @@ var UpstashStore = class extends MastraStorage {
925
767
  };
926
768
  }
927
769
  }
928
- async persistWorkflowSnapshot(params) {
929
- const { namespace = "workflows", workflowName, runId, snapshot } = params;
770
+ async getResourceById({ resourceId }) {
930
771
  try {
931
- await this.insert({
932
- tableName: TABLE_WORKFLOW_SNAPSHOT,
933
- record: {
934
- namespace,
935
- workflow_name: workflowName,
936
- run_id: runId,
937
- snapshot,
772
+ const key = `${TABLE_RESOURCES}:${resourceId}`;
773
+ const data = await this.client.get(key);
774
+ if (!data) {
775
+ return null;
776
+ }
777
+ return {
778
+ ...data,
779
+ createdAt: new Date(data.createdAt),
780
+ updatedAt: new Date(data.updatedAt),
781
+ // Ensure workingMemory is always returned as a string, regardless of automatic parsing
782
+ workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory,
783
+ metadata: typeof data.metadata === "string" ? JSON.parse(data.metadata) : data.metadata
784
+ };
785
+ } catch (error) {
786
+ this.logger.error("Error getting resource by ID:", error);
787
+ throw error;
788
+ }
789
+ }
790
+ async saveResource({ resource }) {
791
+ try {
792
+ const key = `${TABLE_RESOURCES}:${resource.id}`;
793
+ const serializedResource = {
794
+ ...resource,
795
+ metadata: JSON.stringify(resource.metadata),
796
+ createdAt: resource.createdAt.toISOString(),
797
+ updatedAt: resource.updatedAt.toISOString()
798
+ };
799
+ await this.client.set(key, serializedResource);
800
+ return resource;
801
+ } catch (error) {
802
+ this.logger.error("Error saving resource:", error);
803
+ throw error;
804
+ }
805
+ }
806
+ async updateResource({
807
+ resourceId,
808
+ workingMemory,
809
+ metadata
810
+ }) {
811
+ try {
812
+ const existingResource = await this.getResourceById({ resourceId });
813
+ if (!existingResource) {
814
+ const newResource = {
815
+ id: resourceId,
816
+ workingMemory,
817
+ metadata: metadata || {},
938
818
  createdAt: /* @__PURE__ */ new Date(),
939
819
  updatedAt: /* @__PURE__ */ new Date()
820
+ };
821
+ return this.saveResource({ resource: newResource });
822
+ }
823
+ const updatedResource = {
824
+ ...existingResource,
825
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
826
+ metadata: {
827
+ ...existingResource.metadata,
828
+ ...metadata
829
+ },
830
+ updatedAt: /* @__PURE__ */ new Date()
831
+ };
832
+ await this.saveResource({ resource: updatedResource });
833
+ return updatedResource;
834
+ } catch (error) {
835
+ this.logger.error("Error updating resource:", error);
836
+ throw error;
837
+ }
838
+ }
839
+ async updateMessages(args) {
840
+ const { messages } = args;
841
+ if (messages.length === 0) {
842
+ return [];
843
+ }
844
+ try {
845
+ const messageIds = messages.map((m) => m.id);
846
+ const existingMessages = [];
847
+ const messageIdToKey = {};
848
+ for (const messageId of messageIds) {
849
+ const pattern = getMessageKey("*", messageId);
850
+ const keys = await this.operations.scanKeys(pattern);
851
+ for (const key of keys) {
852
+ const message = await this.client.get(key);
853
+ if (message && message.id === messageId) {
854
+ existingMessages.push(message);
855
+ messageIdToKey[messageId] = key;
856
+ break;
857
+ }
940
858
  }
941
- });
859
+ }
860
+ if (existingMessages.length === 0) {
861
+ return [];
862
+ }
863
+ const threadIdsToUpdate = /* @__PURE__ */ new Set();
864
+ const pipeline = this.client.pipeline();
865
+ for (const existingMessage of existingMessages) {
866
+ const updatePayload = messages.find((m) => m.id === existingMessage.id);
867
+ if (!updatePayload) continue;
868
+ const { id, ...fieldsToUpdate } = updatePayload;
869
+ if (Object.keys(fieldsToUpdate).length === 0) continue;
870
+ threadIdsToUpdate.add(existingMessage.threadId);
871
+ if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
872
+ threadIdsToUpdate.add(updatePayload.threadId);
873
+ }
874
+ const updatedMessage = { ...existingMessage };
875
+ if (fieldsToUpdate.content) {
876
+ const existingContent = existingMessage.content;
877
+ const newContent = {
878
+ ...existingContent,
879
+ ...fieldsToUpdate.content,
880
+ // Deep merge metadata if it exists on both
881
+ ...existingContent?.metadata && fieldsToUpdate.content.metadata ? {
882
+ metadata: {
883
+ ...existingContent.metadata,
884
+ ...fieldsToUpdate.content.metadata
885
+ }
886
+ } : {}
887
+ };
888
+ updatedMessage.content = newContent;
889
+ }
890
+ for (const key2 in fieldsToUpdate) {
891
+ if (Object.prototype.hasOwnProperty.call(fieldsToUpdate, key2) && key2 !== "content") {
892
+ updatedMessage[key2] = fieldsToUpdate[key2];
893
+ }
894
+ }
895
+ const key = messageIdToKey[id];
896
+ if (key) {
897
+ if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
898
+ const oldThreadMessagesKey = getThreadMessagesKey(existingMessage.threadId);
899
+ pipeline.zrem(oldThreadMessagesKey, id);
900
+ pipeline.del(key);
901
+ const newKey = getMessageKey(updatePayload.threadId, id);
902
+ pipeline.set(newKey, updatedMessage);
903
+ const newThreadMessagesKey = getThreadMessagesKey(updatePayload.threadId);
904
+ const score = updatedMessage._index !== void 0 ? updatedMessage._index : new Date(updatedMessage.createdAt).getTime();
905
+ pipeline.zadd(newThreadMessagesKey, { score, member: id });
906
+ } else {
907
+ pipeline.set(key, updatedMessage);
908
+ }
909
+ }
910
+ }
911
+ const now = /* @__PURE__ */ new Date();
912
+ for (const threadId of threadIdsToUpdate) {
913
+ if (threadId) {
914
+ const threadKey = getKey(TABLE_THREADS, { id: threadId });
915
+ const existingThread = await this.client.get(threadKey);
916
+ if (existingThread) {
917
+ const updatedThread = {
918
+ ...existingThread,
919
+ updatedAt: now
920
+ };
921
+ pipeline.set(threadKey, processRecord(TABLE_THREADS, updatedThread).processedRecord);
922
+ }
923
+ }
924
+ }
925
+ await pipeline.exec();
926
+ const updatedMessages = [];
927
+ for (const messageId of messageIds) {
928
+ const key = messageIdToKey[messageId];
929
+ if (key) {
930
+ const updatedMessage = await this.client.get(key);
931
+ if (updatedMessage) {
932
+ const v2e = updatedMessage;
933
+ updatedMessages.push(v2e);
934
+ }
935
+ }
936
+ }
937
+ return updatedMessages;
942
938
  } catch (error) {
943
939
  throw new MastraError(
944
940
  {
945
- id: "STORAGE_UPSTASH_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
941
+ id: "STORAGE_UPSTASH_STORAGE_UPDATE_MESSAGES_FAILED",
946
942
  domain: ErrorDomain.STORAGE,
947
943
  category: ErrorCategory.THIRD_PARTY,
948
944
  details: {
949
- namespace,
950
- workflowName,
951
- runId
945
+ messageIds: messages.map((m) => m.id).join(",")
952
946
  }
953
947
  },
954
948
  error
955
949
  );
956
950
  }
957
951
  }
958
- async loadWorkflowSnapshot(params) {
959
- const { namespace = "workflows", workflowName, runId } = params;
960
- const key = this.getKey(TABLE_WORKFLOW_SNAPSHOT, {
961
- namespace,
962
- workflow_name: workflowName,
963
- run_id: runId
964
- });
952
+ };
953
+ var StoreOperationsUpstash = class extends StoreOperations {
954
+ client;
955
+ constructor({ client }) {
956
+ super();
957
+ this.client = client;
958
+ }
959
+ async createTable({
960
+ tableName: _tableName,
961
+ schema: _schema
962
+ }) {
963
+ }
964
+ async alterTable({
965
+ tableName: _tableName,
966
+ schema: _schema,
967
+ ifNotExists: _ifNotExists
968
+ }) {
969
+ }
970
+ async clearTable({ tableName }) {
971
+ const pattern = `${tableName}:*`;
965
972
  try {
966
- const data = await this.redis.get(key);
967
- if (!data) return null;
968
- return data.snapshot;
973
+ await this.scanAndDelete(pattern);
969
974
  } catch (error) {
970
975
  throw new MastraError(
971
976
  {
972
- id: "STORAGE_UPSTASH_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
977
+ id: "STORAGE_UPSTASH_STORAGE_CLEAR_TABLE_FAILED",
973
978
  domain: ErrorDomain.STORAGE,
974
979
  category: ErrorCategory.THIRD_PARTY,
975
980
  details: {
976
- namespace,
977
- workflowName,
978
- runId
981
+ tableName
979
982
  }
980
983
  },
981
984
  error
982
985
  );
983
986
  }
984
987
  }
985
- /**
986
- * Get all evaluations with pagination and total count
987
- * @param options Pagination and filtering options
988
- * @returns Object with evals array and total count
989
- */
990
- async getEvals(options) {
988
+ async dropTable({ tableName }) {
989
+ return this.clearTable({ tableName });
990
+ }
991
+ async insert({ tableName, record }) {
992
+ const { key, processedRecord } = processRecord(tableName, record);
991
993
  try {
992
- const { agentName, type, page = 0, perPage = 100, dateRange } = options || {};
993
- const fromDate = dateRange?.start;
994
- const toDate = dateRange?.end;
995
- const pattern = `${TABLE_EVALS}:*`;
996
- const keys = await this.scanKeys(pattern);
997
- if (keys.length === 0) {
998
- return {
999
- evals: [],
1000
- total: 0,
994
+ await this.client.set(key, processedRecord);
995
+ } catch (error) {
996
+ throw new MastraError(
997
+ {
998
+ id: "STORAGE_UPSTASH_STORAGE_INSERT_FAILED",
999
+ domain: ErrorDomain.STORAGE,
1000
+ category: ErrorCategory.THIRD_PARTY,
1001
+ details: {
1002
+ tableName
1003
+ }
1004
+ },
1005
+ error
1006
+ );
1007
+ }
1008
+ }
1009
+ async batchInsert(input) {
1010
+ const { tableName, records } = input;
1011
+ if (!records.length) return;
1012
+ const batchSize = 1e3;
1013
+ try {
1014
+ for (let i = 0; i < records.length; i += batchSize) {
1015
+ const batch = records.slice(i, i + batchSize);
1016
+ const pipeline = this.client.pipeline();
1017
+ for (const record of batch) {
1018
+ const { key, processedRecord } = processRecord(tableName, record);
1019
+ pipeline.set(key, processedRecord);
1020
+ }
1021
+ await pipeline.exec();
1022
+ }
1023
+ } catch (error) {
1024
+ throw new MastraError(
1025
+ {
1026
+ id: "STORAGE_UPSTASH_STORAGE_BATCH_INSERT_FAILED",
1027
+ domain: ErrorDomain.STORAGE,
1028
+ category: ErrorCategory.THIRD_PARTY,
1029
+ details: {
1030
+ tableName
1031
+ }
1032
+ },
1033
+ error
1034
+ );
1035
+ }
1036
+ }
1037
+ async load({ tableName, keys }) {
1038
+ const key = getKey(tableName, keys);
1039
+ try {
1040
+ const data = await this.client.get(key);
1041
+ return data || null;
1042
+ } catch (error) {
1043
+ throw new MastraError(
1044
+ {
1045
+ id: "STORAGE_UPSTASH_STORAGE_LOAD_FAILED",
1046
+ domain: ErrorDomain.STORAGE,
1047
+ category: ErrorCategory.THIRD_PARTY,
1048
+ details: {
1049
+ tableName
1050
+ }
1051
+ },
1052
+ error
1053
+ );
1054
+ }
1055
+ }
1056
+ async hasColumn(_tableName, _column) {
1057
+ return true;
1058
+ }
1059
+ async scanKeys(pattern, batchSize = 1e4) {
1060
+ let cursor = "0";
1061
+ let keys = [];
1062
+ do {
1063
+ const [nextCursor, batch] = await this.client.scan(cursor, {
1064
+ match: pattern,
1065
+ count: batchSize
1066
+ });
1067
+ keys.push(...batch);
1068
+ cursor = nextCursor;
1069
+ } while (cursor !== "0");
1070
+ return keys;
1071
+ }
1072
+ async scanAndDelete(pattern, batchSize = 1e4) {
1073
+ let cursor = "0";
1074
+ let totalDeleted = 0;
1075
+ do {
1076
+ const [nextCursor, keys] = await this.client.scan(cursor, {
1077
+ match: pattern,
1078
+ count: batchSize
1079
+ });
1080
+ if (keys.length > 0) {
1081
+ await this.client.del(...keys);
1082
+ totalDeleted += keys.length;
1083
+ }
1084
+ cursor = nextCursor;
1085
+ } while (cursor !== "0");
1086
+ return totalDeleted;
1087
+ }
1088
+ };
1089
+ function transformScoreRow(row) {
1090
+ const parseField = (v) => {
1091
+ if (typeof v === "string") {
1092
+ try {
1093
+ return JSON.parse(v);
1094
+ } catch {
1095
+ return v;
1096
+ }
1097
+ }
1098
+ return v;
1099
+ };
1100
+ return {
1101
+ ...row,
1102
+ scorer: parseField(row.scorer),
1103
+ extractStepResult: parseField(row.extractStepResult),
1104
+ analyzeStepResult: parseField(row.analyzeStepResult),
1105
+ metadata: parseField(row.metadata),
1106
+ input: parseField(row.input),
1107
+ output: parseField(row.output),
1108
+ additionalContext: parseField(row.additionalContext),
1109
+ runtimeContext: parseField(row.runtimeContext),
1110
+ entity: parseField(row.entity),
1111
+ createdAt: row.createdAt,
1112
+ updatedAt: row.updatedAt
1113
+ };
1114
+ }
1115
+ var ScoresUpstash = class extends ScoresStorage {
1116
+ client;
1117
+ operations;
1118
+ constructor({ client, operations }) {
1119
+ super();
1120
+ this.client = client;
1121
+ this.operations = operations;
1122
+ }
1123
+ async getScoreById({ id }) {
1124
+ try {
1125
+ const data = await this.operations.load({
1126
+ tableName: TABLE_SCORERS,
1127
+ keys: { id }
1128
+ });
1129
+ if (!data) return null;
1130
+ return transformScoreRow(data);
1131
+ } catch (error) {
1132
+ throw new MastraError(
1133
+ {
1134
+ id: "STORAGE_UPSTASH_STORAGE_GET_SCORE_BY_ID_FAILED",
1135
+ domain: ErrorDomain.STORAGE,
1136
+ category: ErrorCategory.THIRD_PARTY,
1137
+ details: { id }
1138
+ },
1139
+ error
1140
+ );
1141
+ }
1142
+ }
1143
+ async getScoresByScorerId({
1144
+ scorerId,
1145
+ pagination = { page: 0, perPage: 20 }
1146
+ }) {
1147
+ const pattern = `${TABLE_SCORERS}:*`;
1148
+ const keys = await this.operations.scanKeys(pattern);
1149
+ if (keys.length === 0) {
1150
+ return {
1151
+ scores: [],
1152
+ pagination: { total: 0, page: pagination.page, perPage: pagination.perPage, hasMore: false }
1153
+ };
1154
+ }
1155
+ const pipeline = this.client.pipeline();
1156
+ keys.forEach((key) => pipeline.get(key));
1157
+ const results = await pipeline.exec();
1158
+ const filtered = results.map((row) => row).filter((row) => !!row && typeof row === "object" && row.scorerId === scorerId);
1159
+ const total = filtered.length;
1160
+ const { page, perPage } = pagination;
1161
+ const start = page * perPage;
1162
+ const end = start + perPage;
1163
+ const paged = filtered.slice(start, end);
1164
+ const scores = paged.map((row) => transformScoreRow(row));
1165
+ return {
1166
+ scores,
1167
+ pagination: {
1168
+ total,
1169
+ page,
1170
+ perPage,
1171
+ hasMore: end < total
1172
+ }
1173
+ };
1174
+ }
1175
+ async saveScore(score) {
1176
+ const { key, processedRecord } = processRecord(TABLE_SCORERS, score);
1177
+ try {
1178
+ await this.client.set(key, processedRecord);
1179
+ return { score };
1180
+ } catch (error) {
1181
+ throw new MastraError(
1182
+ {
1183
+ id: "STORAGE_UPSTASH_STORAGE_SAVE_SCORE_FAILED",
1184
+ domain: ErrorDomain.STORAGE,
1185
+ category: ErrorCategory.THIRD_PARTY,
1186
+ details: { id: score.id }
1187
+ },
1188
+ error
1189
+ );
1190
+ }
1191
+ }
1192
+ async getScoresByRunId({
1193
+ runId,
1194
+ pagination = { page: 0, perPage: 20 }
1195
+ }) {
1196
+ const pattern = `${TABLE_SCORERS}:*`;
1197
+ const keys = await this.operations.scanKeys(pattern);
1198
+ if (keys.length === 0) {
1199
+ return {
1200
+ scores: [],
1201
+ pagination: { total: 0, page: pagination.page, perPage: pagination.perPage, hasMore: false }
1202
+ };
1203
+ }
1204
+ const pipeline = this.client.pipeline();
1205
+ keys.forEach((key) => pipeline.get(key));
1206
+ const results = await pipeline.exec();
1207
+ const filtered = results.map((row) => row).filter((row) => !!row && typeof row === "object" && row.runId === runId);
1208
+ const total = filtered.length;
1209
+ const { page, perPage } = pagination;
1210
+ const start = page * perPage;
1211
+ const end = start + perPage;
1212
+ const paged = filtered.slice(start, end);
1213
+ const scores = paged.map((row) => transformScoreRow(row));
1214
+ return {
1215
+ scores,
1216
+ pagination: {
1217
+ total,
1218
+ page,
1219
+ perPage,
1220
+ hasMore: end < total
1221
+ }
1222
+ };
1223
+ }
1224
+ async getScoresByEntityId({
1225
+ entityId,
1226
+ entityType,
1227
+ pagination = { page: 0, perPage: 20 }
1228
+ }) {
1229
+ const pattern = `${TABLE_SCORERS}:*`;
1230
+ const keys = await this.operations.scanKeys(pattern);
1231
+ if (keys.length === 0) {
1232
+ return {
1233
+ scores: [],
1234
+ pagination: { total: 0, page: pagination.page, perPage: pagination.perPage, hasMore: false }
1235
+ };
1236
+ }
1237
+ const pipeline = this.client.pipeline();
1238
+ keys.forEach((key) => pipeline.get(key));
1239
+ const results = await pipeline.exec();
1240
+ const filtered = results.map((row) => row).filter((row) => {
1241
+ if (!row || typeof row !== "object") return false;
1242
+ if (row.entityId !== entityId) return false;
1243
+ if (entityType && row.entityType !== entityType) return false;
1244
+ return true;
1245
+ });
1246
+ const total = filtered.length;
1247
+ const { page, perPage } = pagination;
1248
+ const start = page * perPage;
1249
+ const end = start + perPage;
1250
+ const paged = filtered.slice(start, end);
1251
+ const scores = paged.map((row) => transformScoreRow(row));
1252
+ return {
1253
+ scores,
1254
+ pagination: {
1255
+ total,
1256
+ page,
1257
+ perPage,
1258
+ hasMore: end < total
1259
+ }
1260
+ };
1261
+ }
1262
+ };
1263
+ var TracesUpstash = class extends TracesStorage {
1264
+ client;
1265
+ operations;
1266
+ constructor({ client, operations }) {
1267
+ super();
1268
+ this.client = client;
1269
+ this.operations = operations;
1270
+ }
1271
+ /**
1272
+ * @deprecated use getTracesPaginated instead
1273
+ */
1274
+ async getTraces(args) {
1275
+ if (args.fromDate || args.toDate) {
1276
+ args.dateRange = {
1277
+ start: args.fromDate,
1278
+ end: args.toDate
1279
+ };
1280
+ }
1281
+ try {
1282
+ const { traces } = await this.getTracesPaginated(args);
1283
+ return traces;
1284
+ } catch (error) {
1285
+ throw new MastraError(
1286
+ {
1287
+ id: "STORAGE_UPSTASH_STORAGE_GET_TRACES_FAILED",
1288
+ domain: ErrorDomain.STORAGE,
1289
+ category: ErrorCategory.THIRD_PARTY
1290
+ },
1291
+ error
1292
+ );
1293
+ }
1294
+ }
1295
+ async getTracesPaginated(args) {
1296
+ const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
1297
+ const fromDate = dateRange?.start;
1298
+ const toDate = dateRange?.end;
1299
+ try {
1300
+ const pattern = `${TABLE_TRACES}:*`;
1301
+ const keys = await this.operations.scanKeys(pattern);
1302
+ if (keys.length === 0) {
1303
+ return {
1304
+ traces: [],
1305
+ total: 0,
1001
1306
  page,
1002
- perPage,
1307
+ perPage: perPage || 100,
1003
1308
  hasMore: false
1004
1309
  };
1005
1310
  }
1006
- const pipeline = this.redis.pipeline();
1311
+ const pipeline = this.client.pipeline();
1007
1312
  keys.forEach((key) => pipeline.get(key));
1008
1313
  const results = await pipeline.exec();
1009
- let filteredEvals = results.map((result) => result).filter((record) => record !== null && typeof record === "object");
1010
- if (agentName) {
1011
- filteredEvals = filteredEvals.filter((record) => record.agent_name === agentName);
1314
+ let filteredTraces = results.filter(
1315
+ (record) => record !== null && typeof record === "object"
1316
+ );
1317
+ if (name) {
1318
+ filteredTraces = filteredTraces.filter((record) => record.name?.toLowerCase().startsWith(name.toLowerCase()));
1012
1319
  }
1013
- if (type === "test") {
1014
- filteredEvals = filteredEvals.filter((record) => {
1015
- if (!record.test_info) return false;
1016
- try {
1017
- if (typeof record.test_info === "string") {
1018
- const parsedTestInfo = JSON.parse(record.test_info);
1019
- return parsedTestInfo && typeof parsedTestInfo === "object" && "testPath" in parsedTestInfo;
1020
- }
1021
- return typeof record.test_info === "object" && "testPath" in record.test_info;
1022
- } catch {
1023
- return false;
1024
- }
1025
- });
1026
- } else if (type === "live") {
1027
- filteredEvals = filteredEvals.filter((record) => {
1028
- if (!record.test_info) return true;
1029
- try {
1030
- if (typeof record.test_info === "string") {
1031
- const parsedTestInfo = JSON.parse(record.test_info);
1032
- return !(parsedTestInfo && typeof parsedTestInfo === "object" && "testPath" in parsedTestInfo);
1033
- }
1034
- return !(typeof record.test_info === "object" && "testPath" in record.test_info);
1035
- } catch {
1036
- return true;
1037
- }
1320
+ if (scope) {
1321
+ filteredTraces = filteredTraces.filter((record) => record.scope === scope);
1322
+ }
1323
+ if (attributes) {
1324
+ filteredTraces = filteredTraces.filter((record) => {
1325
+ const recordAttributes = record.attributes;
1326
+ if (!recordAttributes) return false;
1327
+ const parsedAttributes = typeof recordAttributes === "string" ? JSON.parse(recordAttributes) : recordAttributes;
1328
+ return Object.entries(attributes).every(([key, value]) => parsedAttributes[key] === value);
1038
1329
  });
1039
1330
  }
1331
+ if (filters) {
1332
+ filteredTraces = filteredTraces.filter(
1333
+ (record) => Object.entries(filters).every(([key, value]) => record[key] === value)
1334
+ );
1335
+ }
1040
1336
  if (fromDate) {
1041
- filteredEvals = filteredEvals.filter((record) => {
1042
- const createdAt = new Date(record.created_at || record.createdAt || 0);
1043
- return createdAt.getTime() >= fromDate.getTime();
1044
- });
1337
+ filteredTraces = filteredTraces.filter(
1338
+ (record) => new Date(record.createdAt).getTime() >= new Date(fromDate).getTime()
1339
+ );
1045
1340
  }
1046
1341
  if (toDate) {
1047
- filteredEvals = filteredEvals.filter((record) => {
1048
- const createdAt = new Date(record.created_at || record.createdAt || 0);
1049
- return createdAt.getTime() <= toDate.getTime();
1050
- });
1342
+ filteredTraces = filteredTraces.filter(
1343
+ (record) => new Date(record.createdAt).getTime() <= new Date(toDate).getTime()
1344
+ );
1051
1345
  }
1052
- filteredEvals.sort((a, b) => {
1053
- const dateA = new Date(a.created_at || a.createdAt || 0).getTime();
1054
- const dateB = new Date(b.created_at || b.createdAt || 0).getTime();
1055
- return dateB - dateA;
1056
- });
1057
- const total = filteredEvals.length;
1058
- const start = page * perPage;
1059
- const end = start + perPage;
1060
- const paginatedEvals = filteredEvals.slice(start, end);
1346
+ filteredTraces.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
1347
+ const transformedTraces = filteredTraces.map((record) => ({
1348
+ id: record.id,
1349
+ parentSpanId: record.parentSpanId,
1350
+ traceId: record.traceId,
1351
+ name: record.name,
1352
+ scope: record.scope,
1353
+ kind: record.kind,
1354
+ status: parseJSON(record.status),
1355
+ events: parseJSON(record.events),
1356
+ links: parseJSON(record.links),
1357
+ attributes: parseJSON(record.attributes),
1358
+ startTime: record.startTime,
1359
+ endTime: record.endTime,
1360
+ other: parseJSON(record.other),
1361
+ createdAt: ensureDate(record.createdAt)
1362
+ }));
1363
+ const total = transformedTraces.length;
1364
+ const resolvedPerPage = perPage || 100;
1365
+ const start = page * resolvedPerPage;
1366
+ const end = start + resolvedPerPage;
1367
+ const paginatedTraces = transformedTraces.slice(start, end);
1061
1368
  const hasMore = end < total;
1062
- const evals = paginatedEvals.map((record) => this.transformEvalRecord(record));
1063
1369
  return {
1064
- evals,
1370
+ traces: paginatedTraces,
1065
1371
  total,
1066
1372
  page,
1067
- perPage,
1373
+ perPage: resolvedPerPage,
1068
1374
  hasMore
1069
1375
  };
1070
1376
  } catch (error) {
1071
- const { page = 0, perPage = 100 } = options || {};
1072
- const mastraError = new MastraError(
1377
+ const mastraError = new MastraError(
1378
+ {
1379
+ id: "STORAGE_UPSTASH_STORAGE_GET_TRACES_PAGINATED_FAILED",
1380
+ domain: ErrorDomain.STORAGE,
1381
+ category: ErrorCategory.THIRD_PARTY,
1382
+ details: {
1383
+ name: args.name || "",
1384
+ scope: args.scope || ""
1385
+ }
1386
+ },
1387
+ error
1388
+ );
1389
+ this.logger?.trackException(mastraError);
1390
+ this.logger.error(mastraError.toString());
1391
+ return {
1392
+ traces: [],
1393
+ total: 0,
1394
+ page,
1395
+ perPage: perPage || 100,
1396
+ hasMore: false
1397
+ };
1398
+ }
1399
+ }
1400
+ async batchTraceInsert(args) {
1401
+ return this.operations.batchInsert({
1402
+ tableName: TABLE_TRACES,
1403
+ records: args.records
1404
+ });
1405
+ }
1406
+ };
1407
+ function parseWorkflowRun(row) {
1408
+ let parsedSnapshot = row.snapshot;
1409
+ if (typeof parsedSnapshot === "string") {
1410
+ try {
1411
+ parsedSnapshot = JSON.parse(row.snapshot);
1412
+ } catch (e) {
1413
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1414
+ }
1415
+ }
1416
+ return {
1417
+ workflowName: row.workflow_name,
1418
+ runId: row.run_id,
1419
+ snapshot: parsedSnapshot,
1420
+ createdAt: ensureDate(row.createdAt),
1421
+ updatedAt: ensureDate(row.updatedAt),
1422
+ resourceId: row.resourceId
1423
+ };
1424
+ }
1425
+ var WorkflowsUpstash = class extends WorkflowsStorage {
1426
+ client;
1427
+ operations;
1428
+ constructor({ client, operations }) {
1429
+ super();
1430
+ this.client = client;
1431
+ this.operations = operations;
1432
+ }
1433
+ async persistWorkflowSnapshot(params) {
1434
+ const { namespace = "workflows", workflowName, runId, snapshot } = params;
1435
+ try {
1436
+ await this.operations.insert({
1437
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
1438
+ record: {
1439
+ namespace,
1440
+ workflow_name: workflowName,
1441
+ run_id: runId,
1442
+ snapshot,
1443
+ createdAt: /* @__PURE__ */ new Date(),
1444
+ updatedAt: /* @__PURE__ */ new Date()
1445
+ }
1446
+ });
1447
+ } catch (error) {
1448
+ throw new MastraError(
1449
+ {
1450
+ id: "STORAGE_UPSTASH_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1451
+ domain: ErrorDomain.STORAGE,
1452
+ category: ErrorCategory.THIRD_PARTY,
1453
+ details: {
1454
+ namespace,
1455
+ workflowName,
1456
+ runId
1457
+ }
1458
+ },
1459
+ error
1460
+ );
1461
+ }
1462
+ }
1463
+ async loadWorkflowSnapshot(params) {
1464
+ const { namespace = "workflows", workflowName, runId } = params;
1465
+ const key = getKey(TABLE_WORKFLOW_SNAPSHOT, {
1466
+ namespace,
1467
+ workflow_name: workflowName,
1468
+ run_id: runId
1469
+ });
1470
+ try {
1471
+ const data = await this.client.get(key);
1472
+ if (!data) return null;
1473
+ return data.snapshot;
1474
+ } catch (error) {
1475
+ throw new MastraError(
1073
1476
  {
1074
- id: "STORAGE_UPSTASH_STORAGE_GET_EVALS_FAILED",
1477
+ id: "STORAGE_UPSTASH_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1075
1478
  domain: ErrorDomain.STORAGE,
1076
1479
  category: ErrorCategory.THIRD_PARTY,
1077
1480
  details: {
1078
- page,
1079
- perPage
1481
+ namespace,
1482
+ workflowName,
1483
+ runId
1484
+ }
1485
+ },
1486
+ error
1487
+ );
1488
+ }
1489
+ }
1490
+ async getWorkflowRunById({
1491
+ runId,
1492
+ workflowName
1493
+ }) {
1494
+ try {
1495
+ const key = getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName, run_id: runId }) + "*";
1496
+ const keys = await this.operations.scanKeys(key);
1497
+ const workflows = await Promise.all(
1498
+ keys.map(async (key2) => {
1499
+ const data2 = await this.client.get(key2);
1500
+ return data2;
1501
+ })
1502
+ );
1503
+ const data = workflows.find((w) => w?.run_id === runId && w?.workflow_name === workflowName);
1504
+ if (!data) return null;
1505
+ return parseWorkflowRun(data);
1506
+ } catch (error) {
1507
+ throw new MastraError(
1508
+ {
1509
+ id: "STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1510
+ domain: ErrorDomain.STORAGE,
1511
+ category: ErrorCategory.THIRD_PARTY,
1512
+ details: {
1513
+ namespace: "workflows",
1514
+ runId,
1515
+ workflowName: workflowName || ""
1080
1516
  }
1081
1517
  },
1082
1518
  error
1083
1519
  );
1084
- this.logger.error(mastraError.toString());
1085
- this.logger?.trackException(mastraError);
1086
- return {
1087
- evals: [],
1088
- total: 0,
1089
- page,
1090
- perPage,
1091
- hasMore: false
1092
- };
1093
1520
  }
1094
1521
  }
1095
1522
  async getWorkflowRuns({
1096
- namespace,
1097
1523
  workflowName,
1098
1524
  fromDate,
1099
1525
  toDate,
1100
1526
  limit,
1101
1527
  offset,
1102
1528
  resourceId
1103
- } = { namespace: "workflows" }) {
1529
+ }) {
1104
1530
  try {
1105
- let pattern = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace }) + ":*";
1531
+ let pattern = getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows" }) + ":*";
1106
1532
  if (workflowName && resourceId) {
1107
- pattern = this.getKey(TABLE_WORKFLOW_SNAPSHOT, {
1108
- namespace,
1533
+ pattern = getKey(TABLE_WORKFLOW_SNAPSHOT, {
1534
+ namespace: "workflows",
1109
1535
  workflow_name: workflowName,
1110
1536
  run_id: "*",
1111
1537
  resourceId
1112
1538
  });
1113
1539
  } else if (workflowName) {
1114
- pattern = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: workflowName }) + ":*";
1540
+ pattern = getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName }) + ":*";
1115
1541
  } else if (resourceId) {
1116
- pattern = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: "*", run_id: "*", resourceId });
1542
+ pattern = getKey(TABLE_WORKFLOW_SNAPSHOT, {
1543
+ namespace: "workflows",
1544
+ workflow_name: "*",
1545
+ run_id: "*",
1546
+ resourceId
1547
+ });
1117
1548
  }
1118
- const keys = await this.scanKeys(pattern);
1549
+ const keys = await this.operations.scanKeys(pattern);
1119
1550
  if (keys.length === 0) {
1120
1551
  return { runs: [], total: 0 };
1121
1552
  }
1122
- const pipeline = this.redis.pipeline();
1553
+ const pipeline = this.client.pipeline();
1123
1554
  keys.forEach((key) => pipeline.get(key));
1124
1555
  const results = await pipeline.exec();
1125
1556
  let runs = results.map((result) => result).filter(
1126
1557
  (record) => record !== null && record !== void 0 && typeof record === "object" && "workflow_name" in record
1127
- ).filter((record) => !workflowName || record.workflow_name === workflowName).map((w) => this.parseWorkflowRun(w)).filter((w) => {
1558
+ ).filter((record) => !workflowName || record.workflow_name === workflowName).map((w) => parseWorkflowRun(w)).filter((w) => {
1128
1559
  if (fromDate && w.createdAt < fromDate) return false;
1129
1560
  if (toDate && w.createdAt > toDate) return false;
1130
1561
  return true;
@@ -1141,7 +1572,7 @@ var UpstashStore = class extends MastraStorage {
1141
1572
  domain: ErrorDomain.STORAGE,
1142
1573
  category: ErrorCategory.THIRD_PARTY,
1143
1574
  details: {
1144
- namespace,
1575
+ namespace: "workflows",
1145
1576
  workflowName: workflowName || "",
1146
1577
  resourceId: resourceId || ""
1147
1578
  }
@@ -1150,113 +1581,203 @@ var UpstashStore = class extends MastraStorage {
1150
1581
  );
1151
1582
  }
1152
1583
  }
1584
+ };
1585
+
1586
+ // src/storage/index.ts
1587
+ var UpstashStore = class extends MastraStorage {
1588
+ redis;
1589
+ stores;
1590
+ constructor(config) {
1591
+ super({ name: "Upstash" });
1592
+ this.redis = new Redis({
1593
+ url: config.url,
1594
+ token: config.token
1595
+ });
1596
+ const operations = new StoreOperationsUpstash({ client: this.redis });
1597
+ const traces = new TracesUpstash({ client: this.redis, operations });
1598
+ const scores = new ScoresUpstash({ client: this.redis, operations });
1599
+ const workflows = new WorkflowsUpstash({ client: this.redis, operations });
1600
+ const memory = new StoreMemoryUpstash({ client: this.redis, operations });
1601
+ const legacyEvals = new StoreLegacyEvalsUpstash({ client: this.redis, operations });
1602
+ this.stores = {
1603
+ operations,
1604
+ traces,
1605
+ scores,
1606
+ workflows,
1607
+ memory,
1608
+ legacyEvals
1609
+ };
1610
+ }
1611
+ get supports() {
1612
+ return {
1613
+ selectByIncludeResourceScope: true,
1614
+ resourceWorkingMemory: true,
1615
+ hasColumn: false,
1616
+ createTable: false
1617
+ };
1618
+ }
1619
+ /**
1620
+ * @deprecated Use getEvals instead
1621
+ */
1622
+ async getEvalsByAgentName(agentName, type) {
1623
+ return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
1624
+ }
1625
+ /**
1626
+ * Get all evaluations with pagination and total count
1627
+ * @param options Pagination and filtering options
1628
+ * @returns Object with evals array and total count
1629
+ */
1630
+ async getEvals(options) {
1631
+ return this.stores.legacyEvals.getEvals(options);
1632
+ }
1633
+ /**
1634
+ * @deprecated use getTracesPaginated instead
1635
+ */
1636
+ async getTraces(args) {
1637
+ return this.stores.traces.getTraces(args);
1638
+ }
1639
+ async getTracesPaginated(args) {
1640
+ return this.stores.traces.getTracesPaginated(args);
1641
+ }
1642
+ async batchTraceInsert(args) {
1643
+ return this.stores.traces.batchTraceInsert(args);
1644
+ }
1645
+ async createTable({
1646
+ tableName,
1647
+ schema
1648
+ }) {
1649
+ return this.stores.operations.createTable({ tableName, schema });
1650
+ }
1651
+ /**
1652
+ * No-op: This backend is schemaless and does not require schema changes.
1653
+ * @param tableName Name of the table
1654
+ * @param schema Schema of the table
1655
+ * @param ifNotExists Array of column names to add if they don't exist
1656
+ */
1657
+ async alterTable(args) {
1658
+ return this.stores.operations.alterTable(args);
1659
+ }
1660
+ async clearTable({ tableName }) {
1661
+ return this.stores.operations.clearTable({ tableName });
1662
+ }
1663
+ async dropTable({ tableName }) {
1664
+ return this.stores.operations.dropTable({ tableName });
1665
+ }
1666
+ async insert({ tableName, record }) {
1667
+ return this.stores.operations.insert({ tableName, record });
1668
+ }
1669
+ async batchInsert(input) {
1670
+ return this.stores.operations.batchInsert(input);
1671
+ }
1672
+ async load({ tableName, keys }) {
1673
+ return this.stores.operations.load({ tableName, keys });
1674
+ }
1675
+ async getThreadById({ threadId }) {
1676
+ return this.stores.memory.getThreadById({ threadId });
1677
+ }
1678
+ /**
1679
+ * @deprecated use getThreadsByResourceIdPaginated instead
1680
+ */
1681
+ async getThreadsByResourceId({ resourceId }) {
1682
+ return this.stores.memory.getThreadsByResourceId({ resourceId });
1683
+ }
1684
+ async getThreadsByResourceIdPaginated(args) {
1685
+ return this.stores.memory.getThreadsByResourceIdPaginated(args);
1686
+ }
1687
+ async saveThread({ thread }) {
1688
+ return this.stores.memory.saveThread({ thread });
1689
+ }
1690
+ async updateThread({
1691
+ id,
1692
+ title,
1693
+ metadata
1694
+ }) {
1695
+ return this.stores.memory.updateThread({ id, title, metadata });
1696
+ }
1697
+ async deleteThread({ threadId }) {
1698
+ return this.stores.memory.deleteThread({ threadId });
1699
+ }
1700
+ async saveMessages(args) {
1701
+ return this.stores.memory.saveMessages(args);
1702
+ }
1703
+ async getMessages({
1704
+ threadId,
1705
+ selectBy,
1706
+ format
1707
+ }) {
1708
+ return this.stores.memory.getMessages({ threadId, selectBy, format });
1709
+ }
1710
+ async getMessagesPaginated(args) {
1711
+ return this.stores.memory.getMessagesPaginated(args);
1712
+ }
1713
+ async persistWorkflowSnapshot(params) {
1714
+ return this.stores.workflows.persistWorkflowSnapshot(params);
1715
+ }
1716
+ async loadWorkflowSnapshot(params) {
1717
+ return this.stores.workflows.loadWorkflowSnapshot(params);
1718
+ }
1719
+ async getWorkflowRuns({
1720
+ workflowName,
1721
+ fromDate,
1722
+ toDate,
1723
+ limit,
1724
+ offset,
1725
+ resourceId
1726
+ } = {}) {
1727
+ return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
1728
+ }
1153
1729
  async getWorkflowRunById({
1154
- namespace = "workflows",
1155
1730
  runId,
1156
1731
  workflowName
1157
1732
  }) {
1158
- try {
1159
- const key = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: workflowName, run_id: runId }) + "*";
1160
- const keys = await this.scanKeys(key);
1161
- const workflows = await Promise.all(
1162
- keys.map(async (key2) => {
1163
- const data2 = await this.redis.get(key2);
1164
- return data2;
1165
- })
1166
- );
1167
- const data = workflows.find((w) => w?.run_id === runId && w?.workflow_name === workflowName);
1168
- if (!data) return null;
1169
- return this.parseWorkflowRun(data);
1170
- } catch (error) {
1171
- throw new MastraError(
1172
- {
1173
- id: "STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1174
- domain: ErrorDomain.STORAGE,
1175
- category: ErrorCategory.THIRD_PARTY,
1176
- details: {
1177
- namespace,
1178
- runId,
1179
- workflowName: workflowName || ""
1180
- }
1181
- },
1182
- error
1183
- );
1184
- }
1733
+ return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
1185
1734
  }
1186
1735
  async close() {
1187
1736
  }
1188
- async updateMessages(_args) {
1189
- this.logger.error("updateMessages is not yet implemented in UpstashStore");
1190
- throw new Error("Method not implemented");
1737
+ async updateMessages(args) {
1738
+ return this.stores.memory.updateMessages(args);
1191
1739
  }
1192
1740
  async getResourceById({ resourceId }) {
1193
- try {
1194
- const key = `${TABLE_RESOURCES}:${resourceId}`;
1195
- const data = await this.redis.get(key);
1196
- if (!data) {
1197
- return null;
1198
- }
1199
- return {
1200
- ...data,
1201
- createdAt: new Date(data.createdAt),
1202
- updatedAt: new Date(data.updatedAt),
1203
- // Ensure workingMemory is always returned as a string, regardless of automatic parsing
1204
- workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory,
1205
- metadata: typeof data.metadata === "string" ? JSON.parse(data.metadata) : data.metadata
1206
- };
1207
- } catch (error) {
1208
- this.logger.error("Error getting resource by ID:", error);
1209
- throw error;
1210
- }
1741
+ return this.stores.memory.getResourceById({ resourceId });
1211
1742
  }
1212
1743
  async saveResource({ resource }) {
1213
- try {
1214
- const key = `${TABLE_RESOURCES}:${resource.id}`;
1215
- const serializedResource = {
1216
- ...resource,
1217
- metadata: JSON.stringify(resource.metadata),
1218
- createdAt: resource.createdAt.toISOString(),
1219
- updatedAt: resource.updatedAt.toISOString()
1220
- };
1221
- await this.redis.set(key, serializedResource);
1222
- return resource;
1223
- } catch (error) {
1224
- this.logger.error("Error saving resource:", error);
1225
- throw error;
1226
- }
1744
+ return this.stores.memory.saveResource({ resource });
1227
1745
  }
1228
1746
  async updateResource({
1229
1747
  resourceId,
1230
1748
  workingMemory,
1231
1749
  metadata
1232
1750
  }) {
1233
- try {
1234
- const existingResource = await this.getResourceById({ resourceId });
1235
- if (!existingResource) {
1236
- const newResource = {
1237
- id: resourceId,
1238
- workingMemory,
1239
- metadata: metadata || {},
1240
- createdAt: /* @__PURE__ */ new Date(),
1241
- updatedAt: /* @__PURE__ */ new Date()
1242
- };
1243
- return this.saveResource({ resource: newResource });
1244
- }
1245
- const updatedResource = {
1246
- ...existingResource,
1247
- workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1248
- metadata: {
1249
- ...existingResource.metadata,
1250
- ...metadata
1251
- },
1252
- updatedAt: /* @__PURE__ */ new Date()
1253
- };
1254
- await this.saveResource({ resource: updatedResource });
1255
- return updatedResource;
1256
- } catch (error) {
1257
- this.logger.error("Error updating resource:", error);
1258
- throw error;
1259
- }
1751
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
1752
+ }
1753
+ async getScoreById({ id: _id }) {
1754
+ return this.stores.scores.getScoreById({ id: _id });
1755
+ }
1756
+ async saveScore(score) {
1757
+ return this.stores.scores.saveScore(score);
1758
+ }
1759
+ async getScoresByRunId({
1760
+ runId,
1761
+ pagination
1762
+ }) {
1763
+ return this.stores.scores.getScoresByRunId({ runId, pagination });
1764
+ }
1765
+ async getScoresByEntityId({
1766
+ entityId,
1767
+ entityType,
1768
+ pagination
1769
+ }) {
1770
+ return this.stores.scores.getScoresByEntityId({
1771
+ entityId,
1772
+ entityType,
1773
+ pagination
1774
+ });
1775
+ }
1776
+ async getScoresByScorerId({
1777
+ scorerId,
1778
+ pagination
1779
+ }) {
1780
+ return this.stores.scores.getScoresByScorerId({ scorerId, pagination });
1260
1781
  }
1261
1782
  };
1262
1783
  var UpstashFilterTranslator = class extends BaseFilterTranslator {