@mastra/redis 1.2.2 → 1.3.0

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 CHANGED
@@ -1,1941 +1,1708 @@
1
- 'use strict';
2
-
3
- var agent = require('@mastra/core/agent');
4
- var error = require('@mastra/core/error');
5
- var storage = require('@mastra/core/storage');
6
- var crypto2 = require('crypto');
7
- var evals = require('@mastra/core/evals');
8
- var redis = require('redis');
9
- var cache = require('@mastra/core/cache');
10
-
11
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
-
13
- var crypto2__default = /*#__PURE__*/_interopDefault(crypto2);
14
-
15
- // src/storage/domains/memory/index.ts
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let _mastra_core_agent = require("@mastra/core/agent");
25
+ let _mastra_core_error = require("@mastra/core/error");
26
+ let _mastra_core_storage = require("@mastra/core/storage");
27
+ let crypto$1 = require("crypto");
28
+ crypto$1 = __toESM(crypto$1, 1);
29
+ let _mastra_core_evals = require("@mastra/core/evals");
30
+ let redis = require("redis");
31
+ let _mastra_core_cache = require("@mastra/core/cache");
32
+ //#region src/storage/domains/utils.ts
33
+ /**
34
+ * Generate a Redis key from table name and key parts.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * getKey('mastra_threads', { id: 'thread-123' });
39
+ * // Returns: 'mastra_threads:id:thread-123'
40
+ *
41
+ * getKey('mastra_messages', { threadId: 'thread-123', id: 'msg-456' });
42
+ * // Returns: 'mastra_messages:threadId:thread-123:id:msg-456'
43
+ * ```
44
+ */
16
45
  function getKey(tableName, keys) {
17
- const keyParts = Object.entries(keys).filter(([_, value]) => value !== void 0).map(([key, value]) => {
18
- if (value && typeof value === "object") {
19
- return `${key}:${JSON.stringify(value)}`;
20
- }
21
- return `${key}:${value}`;
22
- });
23
- return `${tableName}:${keyParts.join(":")}`;
46
+ return `${tableName}:${Object.entries(keys).filter(([_, value]) => value !== void 0).map(([key, value]) => {
47
+ if (value && typeof value === "object") return `${key}:${JSON.stringify(value)}`;
48
+ return `${key}:${value}`;
49
+ }).join(":")}`;
24
50
  }
51
+ /**
52
+ * Process a record for storage, generating the appropriate key and serializing dates.
53
+ */
25
54
  function processRecord(tableName, record) {
26
- let key;
27
- if (tableName === storage.TABLE_MESSAGES) {
28
- key = getKey(tableName, { threadId: record.threadId, id: record.id });
29
- } else if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
30
- key = getKey(tableName, {
31
- namespace: record.namespace || "workflows",
32
- workflow_name: record.workflow_name,
33
- run_id: record.run_id,
34
- ...record.resourceId ? { resourceId: record.resourceId } : {}
35
- });
36
- } else {
37
- key = getKey(tableName, { id: record.id });
38
- }
39
- const processedRecord = {
40
- ...record,
41
- createdAt: storage.serializeDate(record.createdAt),
42
- updatedAt: storage.serializeDate(record.updatedAt)
43
- };
44
- return { key, processedRecord };
55
+ let key;
56
+ if (tableName === _mastra_core_storage.TABLE_MESSAGES) key = getKey(tableName, {
57
+ threadId: record.threadId,
58
+ id: record.id
59
+ });
60
+ else if (tableName === _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT) key = getKey(tableName, {
61
+ namespace: record.namespace || "workflows",
62
+ workflow_name: record.workflow_name,
63
+ run_id: record.run_id,
64
+ ...record.resourceId ? { resourceId: record.resourceId } : {}
65
+ });
66
+ else key = getKey(tableName, { id: record.id });
67
+ const processedRecord = {
68
+ ...record,
69
+ createdAt: (0, _mastra_core_storage.serializeDate)(record.createdAt),
70
+ updatedAt: (0, _mastra_core_storage.serializeDate)(record.updatedAt)
71
+ };
72
+ return {
73
+ key,
74
+ processedRecord
75
+ };
45
76
  }
46
-
47
- // src/storage/db/index.ts
77
+ //#endregion
78
+ //#region src/storage/db/index.ts
48
79
  var RedisDB = class {
49
- client;
50
- constructor({ client }) {
51
- this.client = client;
52
- }
53
- getClient() {
54
- return this.client;
55
- }
56
- async insert({ tableName, record }) {
57
- const { key, processedRecord } = processRecord(tableName, record);
58
- try {
59
- await this.client.set(key, JSON.stringify(processedRecord));
60
- } catch (error$1) {
61
- throw new error.MastraError(
62
- {
63
- id: storage.createStorageErrorId("REDIS", "INSERT", "FAILED"),
64
- domain: error.ErrorDomain.STORAGE,
65
- category: error.ErrorCategory.THIRD_PARTY,
66
- details: {
67
- tableName
68
- }
69
- },
70
- error$1
71
- );
72
- }
73
- }
74
- async get({ tableName, keys }) {
75
- const key = getKey(tableName, keys);
76
- try {
77
- const data = await this.client.get(key);
78
- if (!data) {
79
- return null;
80
- }
81
- return JSON.parse(data);
82
- } catch (error$1) {
83
- throw new error.MastraError(
84
- {
85
- id: storage.createStorageErrorId("REDIS", "LOAD", "FAILED"),
86
- domain: error.ErrorDomain.STORAGE,
87
- category: error.ErrorCategory.THIRD_PARTY,
88
- details: {
89
- tableName
90
- }
91
- },
92
- error$1
93
- );
94
- }
95
- }
96
- async scanAndDelete(pattern, batchSize = 1e4) {
97
- let cursor = "0";
98
- let totalDeleted = 0;
99
- do {
100
- const result = await this.client.scan(cursor, { MATCH: pattern, COUNT: batchSize });
101
- if (result.keys.length > 0) {
102
- await this.client.del(result.keys);
103
- totalDeleted += result.keys.length;
104
- }
105
- cursor = result.cursor;
106
- } while (cursor !== "0");
107
- return totalDeleted;
108
- }
109
- async scanKeys(pattern, batchSize = 1e4) {
110
- let cursor = "0";
111
- const keys = [];
112
- do {
113
- const result = await this.client.scan(cursor, { MATCH: pattern, COUNT: batchSize });
114
- keys.push(...result.keys);
115
- cursor = result.cursor;
116
- } while (cursor !== "0");
117
- return keys;
118
- }
119
- async deleteData({ tableName }) {
120
- const pattern = `${tableName}:*`;
121
- try {
122
- await this.scanAndDelete(pattern);
123
- } catch (error$1) {
124
- throw new error.MastraError(
125
- {
126
- id: storage.createStorageErrorId("REDIS", "CLEAR_TABLE", "FAILED"),
127
- domain: error.ErrorDomain.STORAGE,
128
- category: error.ErrorCategory.THIRD_PARTY,
129
- details: {
130
- tableName
131
- }
132
- },
133
- error$1
134
- );
135
- }
136
- }
80
+ client;
81
+ constructor({ client }) {
82
+ this.client = client;
83
+ }
84
+ getClient() {
85
+ return this.client;
86
+ }
87
+ async insert({ tableName, record }) {
88
+ const { key, processedRecord } = processRecord(tableName, record);
89
+ try {
90
+ await this.client.set(key, JSON.stringify(processedRecord));
91
+ } catch (error) {
92
+ throw new _mastra_core_error.MastraError({
93
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "INSERT", "FAILED"),
94
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
95
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
96
+ details: { tableName }
97
+ }, error);
98
+ }
99
+ }
100
+ async get({ tableName, keys }) {
101
+ const key = getKey(tableName, keys);
102
+ try {
103
+ const data = await this.client.get(key);
104
+ if (!data) return null;
105
+ return JSON.parse(data);
106
+ } catch (error) {
107
+ throw new _mastra_core_error.MastraError({
108
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LOAD", "FAILED"),
109
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
110
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
111
+ details: { tableName }
112
+ }, error);
113
+ }
114
+ }
115
+ async scanAndDelete(pattern, batchSize = 1e4) {
116
+ let cursor = "0";
117
+ let totalDeleted = 0;
118
+ do {
119
+ const result = await this.client.scan(cursor, {
120
+ MATCH: pattern,
121
+ COUNT: batchSize
122
+ });
123
+ if (result.keys.length > 0) {
124
+ await this.client.del(result.keys);
125
+ totalDeleted += result.keys.length;
126
+ }
127
+ cursor = result.cursor;
128
+ } while (cursor !== "0");
129
+ return totalDeleted;
130
+ }
131
+ async scanKeys(pattern, batchSize = 1e4) {
132
+ let cursor = "0";
133
+ const keys = [];
134
+ do {
135
+ const result = await this.client.scan(cursor, {
136
+ MATCH: pattern,
137
+ COUNT: batchSize
138
+ });
139
+ keys.push(...result.keys);
140
+ cursor = result.cursor;
141
+ } while (cursor !== "0");
142
+ return keys;
143
+ }
144
+ async deleteData({ tableName }) {
145
+ const pattern = `${tableName}:*`;
146
+ try {
147
+ await this.scanAndDelete(pattern);
148
+ } catch (error) {
149
+ throw new _mastra_core_error.MastraError({
150
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "CLEAR_TABLE", "FAILED"),
151
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
152
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
153
+ details: { tableName }
154
+ }, error);
155
+ }
156
+ }
137
157
  };
138
-
139
- // src/storage/domains/memory/index.ts
140
- var StoreMemoryRedis = class extends storage.MemoryStorage {
141
- client;
142
- db;
143
- constructor(config) {
144
- super();
145
- this.client = config.client;
146
- this.db = new RedisDB({ client: config.client });
147
- }
148
- async dangerouslyClearAll() {
149
- await this.db.deleteData({ tableName: storage.TABLE_THREADS });
150
- await this.db.deleteData({ tableName: storage.TABLE_MESSAGES });
151
- await this.db.deleteData({ tableName: storage.TABLE_RESOURCES });
152
- await this.db.scanAndDelete("msg-idx:*");
153
- await this.db.scanAndDelete("thread:*:messages");
154
- }
155
- async getThreadById({
156
- threadId,
157
- resourceId
158
- }) {
159
- try {
160
- const thread = await this.db.get({
161
- tableName: storage.TABLE_THREADS,
162
- keys: { id: threadId }
163
- });
164
- if (!thread || resourceId !== void 0 && thread.resourceId !== resourceId) {
165
- return null;
166
- }
167
- return {
168
- ...thread,
169
- createdAt: storage.ensureDate(thread.createdAt),
170
- updatedAt: storage.ensureDate(thread.updatedAt),
171
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
172
- };
173
- } catch (error$1) {
174
- throw new error.MastraError(
175
- {
176
- id: storage.createStorageErrorId("REDIS", "GET_THREAD_BY_ID", "FAILED"),
177
- domain: error.ErrorDomain.STORAGE,
178
- category: error.ErrorCategory.THIRD_PARTY,
179
- details: {
180
- threadId
181
- }
182
- },
183
- error$1
184
- );
185
- }
186
- }
187
- async listThreadsByResourceId(args) {
188
- return this.listThreads(args);
189
- }
190
- async listThreads(args) {
191
- const { page = 0, perPage: perPageInput, orderBy, filter } = args;
192
- const { field, direction } = this.parseOrderBy(orderBy);
193
- try {
194
- this.validatePaginationInput(page, perPageInput ?? 100);
195
- } catch (error$1) {
196
- throw new error.MastraError(
197
- {
198
- id: storage.createStorageErrorId("REDIS", "LIST_THREADS", "INVALID_PAGE"),
199
- domain: error.ErrorDomain.STORAGE,
200
- category: error.ErrorCategory.USER,
201
- details: { page, ...perPageInput !== void 0 && { perPage: perPageInput } }
202
- },
203
- error$1 instanceof Error ? error$1 : new Error("Invalid pagination parameters")
204
- );
205
- }
206
- const perPage = storage.normalizePerPage(perPageInput, 100);
207
- try {
208
- this.validateMetadataKeys(filter?.metadata);
209
- } catch (error$1) {
210
- throw new error.MastraError(
211
- {
212
- id: storage.createStorageErrorId("REDIS", "LIST_THREADS", "INVALID_METADATA_KEY"),
213
- domain: error.ErrorDomain.STORAGE,
214
- category: error.ErrorCategory.USER,
215
- details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
216
- },
217
- error$1 instanceof Error ? error$1 : new Error("Invalid metadata key")
218
- );
219
- }
220
- const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
221
- try {
222
- let allThreads = [];
223
- const pattern = `${storage.TABLE_THREADS}:*`;
224
- const keys = await this.db.scanKeys(pattern);
225
- if (keys.length === 0) {
226
- return {
227
- threads: [],
228
- total: 0,
229
- page,
230
- perPage: perPageForResponse,
231
- hasMore: false
232
- };
233
- }
234
- const results = await this.client.mGet(keys);
235
- for (let i = 0; i < results.length; i++) {
236
- const data = results[i];
237
- if (!data) {
238
- continue;
239
- }
240
- const thread = JSON.parse(data);
241
- if (filter?.resourceId && thread.resourceId !== filter.resourceId) {
242
- continue;
243
- }
244
- if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
245
- const threadMetadata = typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata;
246
- const matches = Object.entries(filter.metadata).every(
247
- ([key, value]) => storage.jsonValueEquals(threadMetadata?.[key], value)
248
- );
249
- if (!matches) {
250
- continue;
251
- }
252
- }
253
- allThreads.push({
254
- ...thread,
255
- createdAt: storage.ensureDate(thread.createdAt),
256
- updatedAt: storage.ensureDate(thread.updatedAt),
257
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
258
- });
259
- }
260
- const sortedThreads = this.sortThreads(allThreads, field, direction);
261
- const total = sortedThreads.length;
262
- const end = perPageInput === false ? total : offset + perPage;
263
- const paginatedThreads = sortedThreads.slice(offset, end);
264
- const hasMore = perPageInput === false ? false : end < total;
265
- return {
266
- threads: paginatedThreads,
267
- total,
268
- page,
269
- perPage: perPageForResponse,
270
- hasMore
271
- };
272
- } catch (error$1) {
273
- const mastraError = new error.MastraError(
274
- {
275
- id: storage.createStorageErrorId("REDIS", "LIST_THREADS", "FAILED"),
276
- domain: error.ErrorDomain.STORAGE,
277
- category: error.ErrorCategory.THIRD_PARTY,
278
- details: {
279
- ...filter?.resourceId && { resourceId: filter.resourceId },
280
- hasMetadataFilter: !!filter?.metadata,
281
- page,
282
- perPage
283
- }
284
- },
285
- error$1
286
- );
287
- this.logger.trackException(mastraError);
288
- this.logger.error(mastraError.toString());
289
- return {
290
- threads: [],
291
- total: 0,
292
- page,
293
- perPage: perPageForResponse,
294
- hasMore: false
295
- };
296
- }
297
- }
298
- async saveThread({ thread }) {
299
- try {
300
- await this.db.insert({
301
- tableName: storage.TABLE_THREADS,
302
- record: thread
303
- });
304
- return thread;
305
- } catch (error$1) {
306
- const mastraError = new error.MastraError(
307
- {
308
- id: storage.createStorageErrorId("REDIS", "SAVE_THREAD", "FAILED"),
309
- domain: error.ErrorDomain.STORAGE,
310
- category: error.ErrorCategory.THIRD_PARTY,
311
- details: {
312
- threadId: thread.id
313
- }
314
- },
315
- error$1
316
- );
317
- this.logger.trackException(mastraError);
318
- this.logger.error(mastraError.toString());
319
- throw mastraError;
320
- }
321
- }
322
- async updateThread({
323
- id,
324
- title,
325
- metadata
326
- }) {
327
- const thread = await this.getThreadById({ threadId: id });
328
- if (!thread) {
329
- throw new error.MastraError({
330
- id: storage.createStorageErrorId("REDIS", "UPDATE_THREAD", "FAILED"),
331
- domain: error.ErrorDomain.STORAGE,
332
- category: error.ErrorCategory.USER,
333
- text: `Thread ${id} not found`,
334
- details: {
335
- threadId: id
336
- }
337
- });
338
- }
339
- const updatedThread = {
340
- ...thread,
341
- title,
342
- metadata: {
343
- ...thread.metadata,
344
- ...metadata
345
- },
346
- updatedAt: /* @__PURE__ */ new Date()
347
- };
348
- try {
349
- await this.saveThread({ thread: updatedThread });
350
- return updatedThread;
351
- } catch (error$1) {
352
- throw new error.MastraError(
353
- {
354
- id: storage.createStorageErrorId("REDIS", "UPDATE_THREAD", "FAILED"),
355
- domain: error.ErrorDomain.STORAGE,
356
- category: error.ErrorCategory.THIRD_PARTY,
357
- details: {
358
- threadId: id
359
- }
360
- },
361
- error$1
362
- );
363
- }
364
- }
365
- async deleteThread({ threadId }) {
366
- const threadKey = getKey(storage.TABLE_THREADS, { id: threadId });
367
- const threadMessagesKey = getThreadMessagesKey(threadId);
368
- try {
369
- const messageIds = await this.client.zRange(threadMessagesKey, 0, -1);
370
- const multi = this.client.multi();
371
- multi.del(threadKey);
372
- multi.del(threadMessagesKey);
373
- for (const messageId of messageIds) {
374
- const messageKey = getMessageKey(threadId, messageId);
375
- multi.del(messageKey);
376
- multi.del(getMessageIndexKey(messageId));
377
- }
378
- await multi.exec();
379
- await this.db.scanAndDelete(getMessageKey(threadId, "*"));
380
- } catch (error$1) {
381
- throw new error.MastraError(
382
- {
383
- id: storage.createStorageErrorId("REDIS", "DELETE_THREAD", "FAILED"),
384
- domain: error.ErrorDomain.STORAGE,
385
- category: error.ErrorCategory.THIRD_PARTY,
386
- details: {
387
- threadId
388
- }
389
- },
390
- error$1
391
- );
392
- }
393
- }
394
- async saveMessages(args) {
395
- const { messages } = args;
396
- if (messages.length === 0) {
397
- return { messages: [] };
398
- }
399
- const threadId = messages[0]?.threadId;
400
- try {
401
- if (!threadId) {
402
- throw new Error("Thread ID is required");
403
- }
404
- const thread = await this.getThreadById({ threadId });
405
- if (!thread) {
406
- throw new Error(`Thread ${threadId} not found`);
407
- }
408
- } catch (error$1) {
409
- throw new error.MastraError(
410
- {
411
- id: storage.createStorageErrorId("REDIS", "SAVE_MESSAGES", "INVALID_ARGS"),
412
- domain: error.ErrorDomain.STORAGE,
413
- category: error.ErrorCategory.USER
414
- },
415
- error$1
416
- );
417
- }
418
- const messagesWithIndex = messages.map((message, index) => {
419
- if (!message.threadId) {
420
- throw new Error(
421
- `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
422
- );
423
- }
424
- if (!message.resourceId) {
425
- throw new Error(
426
- `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
427
- );
428
- }
429
- return {
430
- ...message,
431
- _index: index
432
- };
433
- });
434
- const threadKey = getKey(storage.TABLE_THREADS, { id: threadId });
435
- const existingThreadData = await this.client.get(threadKey);
436
- const existingThread = existingThreadData ? JSON.parse(existingThreadData) : null;
437
- try {
438
- const batchSize = 1e3;
439
- const existingThreadIds = await this.client.mGet(
440
- messagesWithIndex.map((message) => getMessageIndexKey(message.id))
441
- );
442
- for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
443
- const batch = messagesWithIndex.slice(i, i + batchSize);
444
- const batchExistingThreadIds = existingThreadIds.slice(i, i + batch.length);
445
- const multi = this.client.multi();
446
- for (const [batchIndex, message] of batch.entries()) {
447
- const key = getMessageKey(message.threadId, message.id);
448
- const score = getMessageScore(message);
449
- const existingThreadId = batchExistingThreadIds[batchIndex];
450
- if (existingThreadId && existingThreadId !== message.threadId) {
451
- const existingMessageKey = getMessageKey(existingThreadId, message.id);
452
- multi.del(existingMessageKey);
453
- multi.zRem(getThreadMessagesKey(existingThreadId), message.id);
454
- }
455
- multi.set(key, JSON.stringify(message));
456
- multi.set(getMessageIndexKey(message.id), message.threadId);
457
- multi.zAdd(getThreadMessagesKey(message.threadId), { score, value: message.id });
458
- }
459
- if (i === 0 && existingThread) {
460
- const updatedThread = {
461
- ...existingThread,
462
- updatedAt: /* @__PURE__ */ new Date()
463
- };
464
- multi.set(threadKey, JSON.stringify(processRecord(storage.TABLE_THREADS, updatedThread).processedRecord));
465
- }
466
- await multi.exec();
467
- }
468
- const list = new agent.MessageList().add(messages, "memory");
469
- return { messages: list.get.all.db() };
470
- } catch (error$1) {
471
- throw new error.MastraError(
472
- {
473
- id: storage.createStorageErrorId("REDIS", "SAVE_MESSAGES", "FAILED"),
474
- domain: error.ErrorDomain.STORAGE,
475
- category: error.ErrorCategory.THIRD_PARTY,
476
- details: {
477
- threadId
478
- }
479
- },
480
- error$1
481
- );
482
- }
483
- }
484
- async getThreadIdForMessage(messageId) {
485
- const indexedThreadId = await this.client.get(getMessageIndexKey(messageId));
486
- if (indexedThreadId) {
487
- return indexedThreadId;
488
- }
489
- const keys = await this.db.scanKeys(getMessageKey("*", messageId));
490
- if (keys.length === 0) {
491
- return null;
492
- }
493
- const messageData = await this.client.get(keys[0]);
494
- if (!messageData) {
495
- return null;
496
- }
497
- const message = JSON.parse(messageData);
498
- if (message.threadId) {
499
- await this.client.set(getMessageIndexKey(messageId), message.threadId);
500
- }
501
- return message.threadId || null;
502
- }
503
- async getIncludedMessages(include) {
504
- if (!include?.length) {
505
- return [];
506
- }
507
- const messageIds = /* @__PURE__ */ new Set();
508
- const messageIdToThreadIds = {};
509
- for (const item of include) {
510
- const itemThreadId = await this.getThreadIdForMessage(item.id);
511
- if (!itemThreadId) {
512
- continue;
513
- }
514
- messageIds.add(item.id);
515
- messageIdToThreadIds[item.id] = itemThreadId;
516
- const itemThreadMessagesKey = getThreadMessagesKey(itemThreadId);
517
- const rank = await this.client.zRank(itemThreadMessagesKey, item.id);
518
- if (rank === null) {
519
- continue;
520
- }
521
- if (item.withPreviousMessages) {
522
- const start = Math.max(0, rank - item.withPreviousMessages);
523
- const prevIds = rank === 0 ? [] : await this.client.zRange(itemThreadMessagesKey, start, rank - 1);
524
- prevIds.forEach((id) => {
525
- messageIds.add(id);
526
- messageIdToThreadIds[id] = itemThreadId;
527
- });
528
- }
529
- if (item.withNextMessages) {
530
- const nextIds = await this.client.zRange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
531
- nextIds.forEach((id) => {
532
- messageIds.add(id);
533
- messageIdToThreadIds[id] = itemThreadId;
534
- });
535
- }
536
- }
537
- if (messageIds.size === 0) {
538
- return [];
539
- }
540
- const keysToFetch = Array.from(messageIds).map((id) => getMessageKey(messageIdToThreadIds[id], id));
541
- const results = await this.client.mGet(keysToFetch);
542
- return results.filter((data) => data !== null).map((data) => JSON.parse(data));
543
- }
544
- parseStoredMessage(storedMessage) {
545
- const defaultMessageContent = { format: 2, parts: [{ type: "text", text: "" }] };
546
- const { _index, ...rest } = storedMessage;
547
- return {
548
- ...rest,
549
- createdAt: new Date(rest.createdAt),
550
- content: rest.content || defaultMessageContent
551
- };
552
- }
553
- async listMessagesById({ messageIds }) {
554
- if (messageIds.length === 0) {
555
- return { messages: [] };
556
- }
557
- try {
558
- const rawMessages = [];
559
- const indexKeys = messageIds.map((id) => getMessageIndexKey(id));
560
- const indexResults = await this.client.mGet(indexKeys);
561
- const indexedIds = [];
562
- const unindexedIds = [];
563
- messageIds.forEach((id, i) => {
564
- const threadId = indexResults[i];
565
- if (threadId) {
566
- indexedIds.push({ messageId: id, threadId });
567
- return;
568
- }
569
- unindexedIds.push(id);
570
- });
571
- if (indexedIds.length > 0) {
572
- const messageKeys = indexedIds.map(({ messageId, threadId }) => getMessageKey(threadId, messageId));
573
- const messageResults = await this.client.mGet(messageKeys);
574
- for (const data of messageResults) {
575
- if (data) {
576
- rawMessages.push(JSON.parse(data));
577
- }
578
- }
579
- }
580
- if (unindexedIds.length > 0) {
581
- const threadKeys = await this.db.scanKeys("thread:*:messages");
582
- const result = await Promise.all(
583
- threadKeys.map(async (threadKey) => {
584
- const threadId = threadKey.split(":")[1];
585
- if (!threadId) {
586
- throw new Error(`Failed to parse thread ID from thread key "${threadKey}"`);
587
- }
588
- const msgKeys = unindexedIds.map((id) => getMessageKey(threadId, id));
589
- return this.client.mGet(msgKeys);
590
- })
591
- );
592
- const foundMessages = result.flat(1).filter((data) => !!data).map((data) => JSON.parse(data));
593
- rawMessages.push(...foundMessages);
594
- if (foundMessages.length > 0) {
595
- const multi = this.client.multi();
596
- foundMessages.forEach((msg) => {
597
- if (msg.threadId) {
598
- multi.set(getMessageIndexKey(msg.id), msg.threadId);
599
- }
600
- });
601
- await multi.exec();
602
- }
603
- }
604
- const list = new agent.MessageList().add(rawMessages.map(this.parseStoredMessage), "memory");
605
- return { messages: list.get.all.db() };
606
- } catch (error$1) {
607
- throw new error.MastraError(
608
- {
609
- id: storage.createStorageErrorId("REDIS", "LIST_MESSAGES_BY_ID", "FAILED"),
610
- domain: error.ErrorDomain.STORAGE,
611
- category: error.ErrorCategory.THIRD_PARTY,
612
- details: {
613
- messageIds: JSON.stringify(messageIds)
614
- }
615
- },
616
- error$1
617
- );
618
- }
619
- }
620
- async listMessages(args) {
621
- const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
622
- const threadIds = Array.isArray(threadId) ? threadId : [threadId];
623
- const threadIdsSet = new Set(threadIds);
624
- if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
625
- throw new error.MastraError(
626
- {
627
- id: storage.createStorageErrorId("REDIS", "LIST_MESSAGES", "INVALID_THREAD_ID"),
628
- domain: error.ErrorDomain.STORAGE,
629
- category: error.ErrorCategory.USER,
630
- details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
631
- },
632
- new Error("threadId must be a non-empty string or array of non-empty strings")
633
- );
634
- }
635
- const perPage = storage.normalizePerPage(perPageInput, 40);
636
- const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
637
- try {
638
- if (page < 0) {
639
- throw new error.MastraError(
640
- {
641
- id: storage.createStorageErrorId("REDIS", "LIST_MESSAGES", "INVALID_PAGE"),
642
- domain: error.ErrorDomain.STORAGE,
643
- category: error.ErrorCategory.USER,
644
- details: { page }
645
- },
646
- new Error("page must be >= 0")
647
- );
648
- }
649
- const { field, direction } = this.parseOrderBy(orderBy, "ASC");
650
- const getFieldValue = (msg) => {
651
- if (field === "createdAt") {
652
- return new Date(msg.createdAt).getTime();
653
- }
654
- const value = msg[field];
655
- if (typeof value === "number") {
656
- return value;
657
- }
658
- if (value instanceof Date) {
659
- return value.getTime();
660
- }
661
- return 0;
662
- };
663
- if (perPage === 0 && (!include || include.length === 0)) {
664
- return {
665
- messages: [],
666
- total: 0,
667
- page,
668
- perPage: perPageForResponse,
669
- hasMore: false
670
- };
671
- }
672
- let includedMessages = [];
673
- if (include && include.length > 0) {
674
- const included = await this.getIncludedMessages(include);
675
- includedMessages = included.map(this.parseStoredMessage);
676
- }
677
- if (perPage === 0 && include && include.length > 0) {
678
- const list2 = new agent.MessageList().add(includedMessages, "memory");
679
- const messages = list2.get.all.db().sort((a, b) => {
680
- const aValue = getFieldValue(a);
681
- const bValue = getFieldValue(b);
682
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
683
- });
684
- return {
685
- messages,
686
- total: 0,
687
- page,
688
- perPage: perPageForResponse,
689
- hasMore: false
690
- };
691
- }
692
- const allMessageIdsWithThreads = [];
693
- for (const tid of threadIds) {
694
- const threadMessagesKey = getThreadMessagesKey(tid);
695
- const msgIds = await this.client.zRange(threadMessagesKey, 0, -1);
696
- for (const mid of msgIds) {
697
- allMessageIdsWithThreads.push({ threadId: tid, messageId: mid });
698
- }
699
- }
700
- if (allMessageIdsWithThreads.length === 0) {
701
- return {
702
- messages: [],
703
- total: 0,
704
- page,
705
- perPage: perPageForResponse,
706
- hasMore: false
707
- };
708
- }
709
- const messageKeys = allMessageIdsWithThreads.map(({ threadId: tid, messageId }) => getMessageKey(tid, messageId));
710
- const results = await this.client.mGet(messageKeys);
711
- let messagesData = results.filter((data) => data !== null).map((data) => JSON.parse(data)).map(this.parseStoredMessage);
712
- if (resourceId) {
713
- messagesData = messagesData.filter((msg) => msg.resourceId === resourceId);
714
- }
715
- messagesData = storage.filterByDateRange(
716
- messagesData,
717
- (msg) => new Date(msg.createdAt),
718
- filter?.dateRange
719
- );
720
- messagesData.sort((a, b) => {
721
- const aValue = getFieldValue(a);
722
- const bValue = getFieldValue(b);
723
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
724
- });
725
- const total = messagesData.length;
726
- const start = offset;
727
- const end = perPageInput === false ? total : start + perPage;
728
- const paginatedMessages = messagesData.slice(start, end);
729
- const messageIdsSet = /* @__PURE__ */ new Set();
730
- const allMessages = [];
731
- for (const msg of paginatedMessages) {
732
- if (messageIdsSet.has(msg.id)) {
733
- continue;
734
- }
735
- allMessages.push(msg);
736
- messageIdsSet.add(msg.id);
737
- }
738
- for (const msg of includedMessages) {
739
- if (messageIdsSet.has(msg.id)) {
740
- continue;
741
- }
742
- allMessages.push(msg);
743
- messageIdsSet.add(msg.id);
744
- }
745
- const list = new agent.MessageList().add(allMessages, "memory");
746
- let finalMessages = list.get.all.db();
747
- finalMessages = finalMessages.sort((a, b) => {
748
- const aValue = getFieldValue(a);
749
- const bValue = getFieldValue(b);
750
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
751
- });
752
- const returnedThreadMessageIds = new Set(
753
- finalMessages.filter((m) => {
754
- return m.threadId && threadIdsSet.has(m.threadId);
755
- }).map((m) => m.id)
756
- );
757
- const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
758
- const hasMore = perPageInput !== false && !allThreadMessagesReturned && end < total;
759
- return {
760
- messages: finalMessages,
761
- total,
762
- page,
763
- perPage: perPageForResponse,
764
- hasMore
765
- };
766
- } catch (error$1) {
767
- const mastraError = new error.MastraError(
768
- {
769
- id: storage.createStorageErrorId("REDIS", "LIST_MESSAGES", "FAILED"),
770
- domain: error.ErrorDomain.STORAGE,
771
- category: error.ErrorCategory.THIRD_PARTY,
772
- details: {
773
- threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
774
- resourceId: resourceId ?? ""
775
- }
776
- },
777
- error$1
778
- );
779
- this.logger.error(mastraError.toString());
780
- this.logger.trackException(mastraError);
781
- return {
782
- messages: [],
783
- total: 0,
784
- page,
785
- perPage: perPageForResponse,
786
- hasMore: false
787
- };
788
- }
789
- }
790
- async getResourceById({ resourceId }) {
791
- try {
792
- const key = `${storage.TABLE_RESOURCES}:${resourceId}`;
793
- const data = await this.client.get(key);
794
- if (!data) {
795
- return null;
796
- }
797
- const resource = JSON.parse(data);
798
- return {
799
- ...resource,
800
- createdAt: new Date(resource.createdAt),
801
- updatedAt: new Date(resource.updatedAt),
802
- workingMemory: typeof resource.workingMemory === "object" ? JSON.stringify(resource.workingMemory) : resource.workingMemory,
803
- metadata: typeof resource.metadata === "string" ? JSON.parse(resource.metadata) : resource.metadata
804
- };
805
- } catch (error) {
806
- this.logger.error("Error getting resource by ID:", error);
807
- throw error;
808
- }
809
- }
810
- async saveResource({ resource }) {
811
- try {
812
- const key = `${storage.TABLE_RESOURCES}:${resource.id}`;
813
- const serializedResource = {
814
- ...resource,
815
- metadata: JSON.stringify(resource.metadata),
816
- createdAt: resource.createdAt.toISOString(),
817
- updatedAt: resource.updatedAt.toISOString()
818
- };
819
- await this.client.set(key, JSON.stringify(serializedResource));
820
- return resource;
821
- } catch (error) {
822
- this.logger.error("Error saving resource:", error);
823
- throw error;
824
- }
825
- }
826
- async updateResource({
827
- resourceId,
828
- workingMemory,
829
- metadata
830
- }) {
831
- try {
832
- const existingResource = await this.getResourceById({ resourceId });
833
- if (!existingResource) {
834
- const newResource = {
835
- id: resourceId,
836
- workingMemory,
837
- metadata: metadata || {},
838
- createdAt: /* @__PURE__ */ new Date(),
839
- updatedAt: /* @__PURE__ */ new Date()
840
- };
841
- return this.saveResource({ resource: newResource });
842
- }
843
- const updatedResource = {
844
- ...existingResource,
845
- workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
846
- metadata: {
847
- ...existingResource.metadata,
848
- ...metadata
849
- },
850
- updatedAt: /* @__PURE__ */ new Date()
851
- };
852
- await this.saveResource({ resource: updatedResource });
853
- return updatedResource;
854
- } catch (error) {
855
- this.logger.error("Error updating resource:", error);
856
- throw error;
857
- }
858
- }
859
- async updateMessages(args) {
860
- const { messages } = args;
861
- if (messages.length === 0) {
862
- return [];
863
- }
864
- try {
865
- const messageIds = messages.map((m) => m.id);
866
- const existingMessages = [];
867
- const messageIdToKey = {};
868
- for (const messageId of messageIds) {
869
- const pattern = getMessageKey("*", messageId);
870
- const keys = await this.db.scanKeys(pattern);
871
- for (const key of keys) {
872
- const data = await this.client.get(key);
873
- if (!data) {
874
- continue;
875
- }
876
- const message = JSON.parse(data);
877
- if (message && message.id === messageId) {
878
- existingMessages.push(message);
879
- messageIdToKey[messageId] = key;
880
- break;
881
- }
882
- }
883
- }
884
- if (existingMessages.length === 0) {
885
- return [];
886
- }
887
- const threadIdsToUpdate = /* @__PURE__ */ new Set();
888
- const multi = this.client.multi();
889
- for (const existingMessage of existingMessages) {
890
- const updatePayload = messages.find((m) => m.id === existingMessage.id);
891
- if (!updatePayload) {
892
- continue;
893
- }
894
- const { id, ...fieldsToUpdate } = updatePayload;
895
- if (Object.keys(fieldsToUpdate).length === 0) {
896
- continue;
897
- }
898
- threadIdsToUpdate.add(existingMessage.threadId);
899
- if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
900
- threadIdsToUpdate.add(updatePayload.threadId);
901
- }
902
- const updatedMessage = { ...existingMessage };
903
- if (fieldsToUpdate.content) {
904
- const existingContent = existingMessage.content;
905
- const newContent = {
906
- ...existingContent,
907
- ...fieldsToUpdate.content,
908
- ...existingContent?.metadata && fieldsToUpdate.content.metadata ? {
909
- metadata: {
910
- ...existingContent.metadata,
911
- ...fieldsToUpdate.content.metadata
912
- }
913
- } : {}
914
- };
915
- updatedMessage.content = newContent;
916
- }
917
- for (const key2 in fieldsToUpdate) {
918
- if (Object.prototype.hasOwnProperty.call(fieldsToUpdate, key2) && key2 !== "content") {
919
- updatedMessage[key2] = fieldsToUpdate[key2];
920
- }
921
- }
922
- const key = messageIdToKey[id];
923
- if (!key) {
924
- continue;
925
- }
926
- if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
927
- multi.zRem(getThreadMessagesKey(existingMessage.threadId), id);
928
- multi.del(key);
929
- const newKey = getMessageKey(updatePayload.threadId, id);
930
- multi.set(newKey, JSON.stringify(updatedMessage));
931
- multi.set(getMessageIndexKey(id), updatePayload.threadId);
932
- const score = getMessageScore(updatedMessage);
933
- multi.zAdd(getThreadMessagesKey(updatePayload.threadId), { score, value: id });
934
- messageIdToKey[id] = newKey;
935
- continue;
936
- }
937
- multi.set(key, JSON.stringify(updatedMessage));
938
- }
939
- const now = /* @__PURE__ */ new Date();
940
- for (const threadId of threadIdsToUpdate) {
941
- if (threadId) {
942
- const threadKey = getKey(storage.TABLE_THREADS, { id: threadId });
943
- const existingThreadData = await this.client.get(threadKey);
944
- if (existingThreadData) {
945
- const existingThread = JSON.parse(existingThreadData);
946
- const updatedThread = {
947
- ...existingThread,
948
- updatedAt: now
949
- };
950
- multi.set(threadKey, JSON.stringify(processRecord(storage.TABLE_THREADS, updatedThread).processedRecord));
951
- }
952
- }
953
- }
954
- await multi.exec();
955
- const updatedMessages = [];
956
- for (const messageId of messageIds) {
957
- const key = messageIdToKey[messageId];
958
- if (key) {
959
- const data = await this.client.get(key);
960
- if (data) {
961
- updatedMessages.push(JSON.parse(data));
962
- }
963
- }
964
- }
965
- return updatedMessages;
966
- } catch (error$1) {
967
- throw new error.MastraError(
968
- {
969
- id: storage.createStorageErrorId("REDIS", "UPDATE_MESSAGES", "FAILED"),
970
- domain: error.ErrorDomain.STORAGE,
971
- category: error.ErrorCategory.THIRD_PARTY,
972
- details: {
973
- messageIds: messages.map((m) => m.id).join(",")
974
- }
975
- },
976
- error$1
977
- );
978
- }
979
- }
980
- async deleteMessages(messageIds) {
981
- if (!messageIds || messageIds.length === 0) {
982
- return;
983
- }
984
- try {
985
- const threadIds = /* @__PURE__ */ new Set();
986
- const messageKeys = [];
987
- const foundMessageIds = [];
988
- const messageIdToThreadId = /* @__PURE__ */ new Map();
989
- const indexKeys = messageIds.map((id) => getMessageIndexKey(id));
990
- const indexResults = await this.client.mGet(indexKeys);
991
- const indexedMessages = [];
992
- const unindexedMessageIds = [];
993
- messageIds.forEach((id, i) => {
994
- const threadId = indexResults[i];
995
- if (threadId) {
996
- indexedMessages.push({ messageId: id, threadId });
997
- return;
998
- }
999
- unindexedMessageIds.push(id);
1000
- });
1001
- for (const { messageId, threadId } of indexedMessages) {
1002
- messageKeys.push(getMessageKey(threadId, messageId));
1003
- foundMessageIds.push(messageId);
1004
- messageIdToThreadId.set(messageId, threadId);
1005
- threadIds.add(threadId);
1006
- }
1007
- for (const messageId of unindexedMessageIds) {
1008
- const pattern = getMessageKey("*", messageId);
1009
- const keys = await this.db.scanKeys(pattern);
1010
- for (const key of keys) {
1011
- const data = await this.client.get(key);
1012
- if (!data) {
1013
- continue;
1014
- }
1015
- const message = JSON.parse(data);
1016
- if (message && message.id === messageId) {
1017
- messageKeys.push(key);
1018
- foundMessageIds.push(messageId);
1019
- if (message.threadId) {
1020
- messageIdToThreadId.set(messageId, message.threadId);
1021
- threadIds.add(message.threadId);
1022
- }
1023
- break;
1024
- }
1025
- }
1026
- }
1027
- if (messageKeys.length === 0) {
1028
- return;
1029
- }
1030
- const multi = this.client.multi();
1031
- for (const key of messageKeys) {
1032
- multi.del(key);
1033
- }
1034
- for (const messageId of foundMessageIds) {
1035
- multi.del(getMessageIndexKey(messageId));
1036
- }
1037
- if (threadIds.size > 0) {
1038
- for (const threadId of threadIds) {
1039
- for (const [msgId, msgThreadId] of messageIdToThreadId) {
1040
- if (msgThreadId === threadId) {
1041
- multi.zRem(getThreadMessagesKey(threadId), msgId);
1042
- }
1043
- }
1044
- const threadKey = getKey(storage.TABLE_THREADS, { id: threadId });
1045
- const threadData = await this.client.get(threadKey);
1046
- if (!threadData) {
1047
- continue;
1048
- }
1049
- const thread = JSON.parse(threadData);
1050
- const updatedThread = { ...thread, updatedAt: /* @__PURE__ */ new Date() };
1051
- multi.set(threadKey, JSON.stringify(processRecord(storage.TABLE_THREADS, updatedThread).processedRecord));
1052
- }
1053
- }
1054
- await multi.exec();
1055
- } catch (error$1) {
1056
- throw new error.MastraError(
1057
- {
1058
- id: storage.createStorageErrorId("REDIS", "DELETE_MESSAGES", "FAILED"),
1059
- domain: error.ErrorDomain.STORAGE,
1060
- category: error.ErrorCategory.THIRD_PARTY,
1061
- details: { messageIds: messageIds.join(", ") }
1062
- },
1063
- error$1
1064
- );
1065
- }
1066
- }
1067
- sortThreads(threads, field, direction) {
1068
- return threads.sort((a, b) => {
1069
- const aValue = new Date(a[field]).getTime();
1070
- const bValue = new Date(b[field]).getTime();
1071
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
1072
- });
1073
- }
1074
- async cloneThread(args) {
1075
- const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args;
1076
- const sourceThread = await this.getThreadById({ threadId: sourceThreadId });
1077
- if (!sourceThread) {
1078
- throw new error.MastraError({
1079
- id: storage.createStorageErrorId("REDIS", "CLONE_THREAD", "SOURCE_NOT_FOUND"),
1080
- domain: error.ErrorDomain.STORAGE,
1081
- category: error.ErrorCategory.USER,
1082
- text: `Source thread with id ${sourceThreadId} not found`,
1083
- details: { sourceThreadId }
1084
- });
1085
- }
1086
- const newThreadId = providedThreadId || crypto.randomUUID();
1087
- const existingThread = await this.getThreadById({ threadId: newThreadId });
1088
- if (existingThread) {
1089
- throw new error.MastraError({
1090
- id: storage.createStorageErrorId("REDIS", "CLONE_THREAD", "THREAD_EXISTS"),
1091
- domain: error.ErrorDomain.STORAGE,
1092
- category: error.ErrorCategory.USER,
1093
- text: `Thread with id ${newThreadId} already exists`,
1094
- details: { newThreadId }
1095
- });
1096
- }
1097
- try {
1098
- const threadMessagesKey = getThreadMessagesKey(sourceThreadId);
1099
- const msgIds = await this.client.zRange(threadMessagesKey, 0, -1);
1100
- const messageKeys = msgIds.map((mid) => getMessageKey(sourceThreadId, mid));
1101
- let sourceMessages = [];
1102
- if (messageKeys.length > 0) {
1103
- const results = await this.client.mGet(messageKeys);
1104
- sourceMessages = results.filter((data) => data !== null).map((data) => {
1105
- const msg = JSON.parse(data);
1106
- return { ...msg, createdAt: new Date(msg.createdAt) };
1107
- });
1108
- }
1109
- if (options?.messageFilter?.startDate || options?.messageFilter?.endDate) {
1110
- sourceMessages = storage.filterByDateRange(sourceMessages, (msg) => new Date(msg.createdAt), {
1111
- start: options.messageFilter?.startDate,
1112
- end: options.messageFilter?.endDate
1113
- });
1114
- }
1115
- if (options?.messageFilter?.messageIds && options.messageFilter.messageIds.length > 0) {
1116
- const messageIdSet = new Set(options.messageFilter.messageIds);
1117
- sourceMessages = sourceMessages.filter((msg) => messageIdSet.has(msg.id));
1118
- }
1119
- sourceMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1120
- if (options?.messageLimit && options.messageLimit > 0 && sourceMessages.length > options.messageLimit) {
1121
- sourceMessages = sourceMessages.slice(-options.messageLimit);
1122
- }
1123
- const now = /* @__PURE__ */ new Date();
1124
- const lastMessageId = sourceMessages.length > 0 ? sourceMessages[sourceMessages.length - 1].id : void 0;
1125
- const cloneMetadata = {
1126
- sourceThreadId,
1127
- clonedAt: now,
1128
- ...lastMessageId && { lastMessageId }
1129
- };
1130
- const newThread = {
1131
- id: newThreadId,
1132
- resourceId: resourceId || sourceThread.resourceId,
1133
- title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : void 0),
1134
- metadata: { ...metadata, clone: cloneMetadata },
1135
- createdAt: now,
1136
- updatedAt: now
1137
- };
1138
- const multi = this.client.multi();
1139
- const threadKey = getKey(storage.TABLE_THREADS, { id: newThreadId });
1140
- multi.set(threadKey, JSON.stringify(processRecord(storage.TABLE_THREADS, newThread).processedRecord));
1141
- const clonedMessages = [];
1142
- const targetResourceId = resourceId || sourceThread.resourceId;
1143
- const newThreadMessagesKey = getThreadMessagesKey(newThreadId);
1144
- for (let i = 0; i < sourceMessages.length; i++) {
1145
- const sourceMsg = sourceMessages[i];
1146
- const newMessageId = crypto.randomUUID();
1147
- const { _index, ...restMsg } = sourceMsg;
1148
- const newMessage = {
1149
- ...restMsg,
1150
- id: newMessageId,
1151
- threadId: newThreadId,
1152
- resourceId: targetResourceId
1153
- };
1154
- const messageKey = getMessageKey(newThreadId, newMessageId);
1155
- multi.set(messageKey, JSON.stringify(newMessage));
1156
- multi.set(getMessageIndexKey(newMessageId), newThreadId);
1157
- const score = getMessageScore({ createdAt: newMessage.createdAt, _index: i });
1158
- multi.zAdd(newThreadMessagesKey, { score, value: newMessageId });
1159
- clonedMessages.push(newMessage);
1160
- }
1161
- await multi.exec();
1162
- return {
1163
- thread: newThread,
1164
- clonedMessages
1165
- };
1166
- } catch (error$1) {
1167
- if (error$1 instanceof error.MastraError) {
1168
- throw error$1;
1169
- }
1170
- throw new error.MastraError(
1171
- {
1172
- id: storage.createStorageErrorId("REDIS", "CLONE_THREAD", "FAILED"),
1173
- domain: error.ErrorDomain.STORAGE,
1174
- category: error.ErrorCategory.THIRD_PARTY,
1175
- details: { sourceThreadId, newThreadId }
1176
- },
1177
- error$1
1178
- );
1179
- }
1180
- }
158
+ //#endregion
159
+ //#region src/storage/domains/memory/index.ts
160
+ var StoreMemoryRedis = class extends _mastra_core_storage.MemoryStorage {
161
+ client;
162
+ db;
163
+ constructor(config) {
164
+ super();
165
+ this.client = config.client;
166
+ this.db = new RedisDB({ client: config.client });
167
+ }
168
+ async dangerouslyClearAll() {
169
+ await this.db.deleteData({ tableName: _mastra_core_storage.TABLE_THREADS });
170
+ await this.db.deleteData({ tableName: _mastra_core_storage.TABLE_MESSAGES });
171
+ await this.db.deleteData({ tableName: _mastra_core_storage.TABLE_RESOURCES });
172
+ await this.db.scanAndDelete("msg-idx:*");
173
+ await this.db.scanAndDelete("thread:*:messages");
174
+ }
175
+ async getThreadById({ threadId, resourceId }) {
176
+ try {
177
+ const thread = await this.db.get({
178
+ tableName: _mastra_core_storage.TABLE_THREADS,
179
+ keys: { id: threadId }
180
+ });
181
+ if (!thread || resourceId !== void 0 && thread.resourceId !== resourceId) return null;
182
+ return {
183
+ ...thread,
184
+ createdAt: (0, _mastra_core_storage.ensureDate)(thread.createdAt),
185
+ updatedAt: (0, _mastra_core_storage.ensureDate)(thread.updatedAt),
186
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
187
+ };
188
+ } catch (error) {
189
+ throw new _mastra_core_error.MastraError({
190
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "GET_THREAD_BY_ID", "FAILED"),
191
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
192
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
193
+ details: { threadId }
194
+ }, error);
195
+ }
196
+ }
197
+ async listThreadsByResourceId(args) {
198
+ return this.listThreads(args);
199
+ }
200
+ async listThreads(args) {
201
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
202
+ const { field, direction } = this.parseOrderBy(orderBy);
203
+ try {
204
+ this.validatePaginationInput(page, perPageInput ?? 100);
205
+ } catch (error) {
206
+ throw new _mastra_core_error.MastraError({
207
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_THREADS", "INVALID_PAGE"),
208
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
209
+ category: _mastra_core_error.ErrorCategory.USER,
210
+ details: {
211
+ page,
212
+ ...perPageInput !== void 0 && { perPage: perPageInput }
213
+ }
214
+ }, error instanceof Error ? error : /* @__PURE__ */ new Error("Invalid pagination parameters"));
215
+ }
216
+ const perPage = (0, _mastra_core_storage.normalizePerPage)(perPageInput, 100);
217
+ try {
218
+ this.validateMetadataKeys(filter?.metadata);
219
+ } catch (error) {
220
+ throw new _mastra_core_error.MastraError({
221
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_THREADS", "INVALID_METADATA_KEY"),
222
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
223
+ category: _mastra_core_error.ErrorCategory.USER,
224
+ details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
225
+ }, error instanceof Error ? error : /* @__PURE__ */ new Error("Invalid metadata key"));
226
+ }
227
+ const { offset, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
228
+ try {
229
+ let allThreads = [];
230
+ const pattern = `${_mastra_core_storage.TABLE_THREADS}:*`;
231
+ const keys = await this.db.scanKeys(pattern);
232
+ if (keys.length === 0) return {
233
+ threads: [],
234
+ total: 0,
235
+ page,
236
+ perPage: perPageForResponse,
237
+ hasMore: false
238
+ };
239
+ const results = await this.client.mGet(keys);
240
+ for (let i = 0; i < results.length; i++) {
241
+ const data = results[i];
242
+ if (!data) continue;
243
+ const thread = JSON.parse(data);
244
+ if (filter?.resourceId && thread.resourceId !== filter.resourceId) continue;
245
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
246
+ const threadMetadata = typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata;
247
+ if (!Object.entries(filter.metadata).every(([key, value]) => (0, _mastra_core_storage.jsonValueEquals)(threadMetadata?.[key], value))) continue;
248
+ }
249
+ allThreads.push({
250
+ ...thread,
251
+ createdAt: (0, _mastra_core_storage.ensureDate)(thread.createdAt),
252
+ updatedAt: (0, _mastra_core_storage.ensureDate)(thread.updatedAt),
253
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
254
+ });
255
+ }
256
+ const sortedThreads = this.sortThreads(allThreads, field, direction);
257
+ const total = sortedThreads.length;
258
+ const end = perPageInput === false ? total : offset + perPage;
259
+ return {
260
+ threads: sortedThreads.slice(offset, end),
261
+ total,
262
+ page,
263
+ perPage: perPageForResponse,
264
+ hasMore: perPageInput === false ? false : end < total
265
+ };
266
+ } catch (error) {
267
+ const mastraError = new _mastra_core_error.MastraError({
268
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_THREADS", "FAILED"),
269
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
270
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
271
+ details: {
272
+ ...filter?.resourceId && { resourceId: filter.resourceId },
273
+ hasMetadataFilter: !!filter?.metadata,
274
+ page,
275
+ perPage
276
+ }
277
+ }, error);
278
+ this.logger.trackException(mastraError);
279
+ this.logger.error(mastraError.toString());
280
+ return {
281
+ threads: [],
282
+ total: 0,
283
+ page,
284
+ perPage: perPageForResponse,
285
+ hasMore: false
286
+ };
287
+ }
288
+ }
289
+ async saveThread({ thread }) {
290
+ try {
291
+ await this.db.insert({
292
+ tableName: _mastra_core_storage.TABLE_THREADS,
293
+ record: thread
294
+ });
295
+ return thread;
296
+ } catch (error) {
297
+ const mastraError = new _mastra_core_error.MastraError({
298
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "SAVE_THREAD", "FAILED"),
299
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
300
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
301
+ details: { threadId: thread.id }
302
+ }, error);
303
+ this.logger.trackException(mastraError);
304
+ this.logger.error(mastraError.toString());
305
+ throw mastraError;
306
+ }
307
+ }
308
+ async updateThread({ id, title, metadata }) {
309
+ const thread = await this.getThreadById({ threadId: id });
310
+ if (!thread) throw new _mastra_core_error.MastraError({
311
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "UPDATE_THREAD", "FAILED"),
312
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
313
+ category: _mastra_core_error.ErrorCategory.USER,
314
+ text: `Thread ${id} not found`,
315
+ details: { threadId: id }
316
+ });
317
+ const updatedThread = {
318
+ ...thread,
319
+ title,
320
+ metadata: {
321
+ ...thread.metadata,
322
+ ...metadata
323
+ },
324
+ updatedAt: /* @__PURE__ */ new Date()
325
+ };
326
+ try {
327
+ await this.saveThread({ thread: updatedThread });
328
+ return updatedThread;
329
+ } catch (error) {
330
+ throw new _mastra_core_error.MastraError({
331
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "UPDATE_THREAD", "FAILED"),
332
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
333
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
334
+ details: { threadId: id }
335
+ }, error);
336
+ }
337
+ }
338
+ async deleteThread({ threadId }) {
339
+ const threadKey = getKey(_mastra_core_storage.TABLE_THREADS, { id: threadId });
340
+ const threadMessagesKey = getThreadMessagesKey(threadId);
341
+ try {
342
+ const messageIds = await this.client.zRange(threadMessagesKey, 0, -1);
343
+ const multi = this.client.multi();
344
+ multi.del(threadKey);
345
+ multi.del(threadMessagesKey);
346
+ for (const messageId of messageIds) {
347
+ const messageKey = getMessageKey(threadId, messageId);
348
+ multi.del(messageKey);
349
+ multi.del(getMessageIndexKey(messageId));
350
+ }
351
+ await multi.exec();
352
+ await this.db.scanAndDelete(getMessageKey(threadId, "*"));
353
+ } catch (error) {
354
+ throw new _mastra_core_error.MastraError({
355
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "DELETE_THREAD", "FAILED"),
356
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
357
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
358
+ details: { threadId }
359
+ }, error);
360
+ }
361
+ }
362
+ async saveMessages(args) {
363
+ const { messages } = args;
364
+ if (messages.length === 0) return { messages: [] };
365
+ const threadId = messages[0]?.threadId;
366
+ try {
367
+ if (!threadId) throw new Error("Thread ID is required");
368
+ if (!await this.getThreadById({ threadId })) throw new Error(`Thread ${threadId} not found`);
369
+ } catch (error) {
370
+ throw new _mastra_core_error.MastraError({
371
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "SAVE_MESSAGES", "INVALID_ARGS"),
372
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
373
+ category: _mastra_core_error.ErrorCategory.USER
374
+ }, error);
375
+ }
376
+ const messagesWithIndex = messages.map((message, index) => {
377
+ if (!message.threadId) throw new Error(`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`);
378
+ if (!message.resourceId) throw new Error(`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`);
379
+ return {
380
+ ...message,
381
+ _index: index
382
+ };
383
+ });
384
+ const threadKey = getKey(_mastra_core_storage.TABLE_THREADS, { id: threadId });
385
+ const existingThreadData = await this.client.get(threadKey);
386
+ const existingThread = existingThreadData ? JSON.parse(existingThreadData) : null;
387
+ try {
388
+ const batchSize = 1e3;
389
+ const existingThreadIds = await this.client.mGet(messagesWithIndex.map((message) => getMessageIndexKey(message.id)));
390
+ for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
391
+ const batch = messagesWithIndex.slice(i, i + batchSize);
392
+ const batchExistingThreadIds = existingThreadIds.slice(i, i + batch.length);
393
+ const multi = this.client.multi();
394
+ for (const [batchIndex, message] of batch.entries()) {
395
+ const key = getMessageKey(message.threadId, message.id);
396
+ const score = getMessageScore(message);
397
+ const existingThreadId = batchExistingThreadIds[batchIndex];
398
+ if (existingThreadId && existingThreadId !== message.threadId) {
399
+ const existingMessageKey = getMessageKey(existingThreadId, message.id);
400
+ multi.del(existingMessageKey);
401
+ multi.zRem(getThreadMessagesKey(existingThreadId), message.id);
402
+ }
403
+ multi.set(key, JSON.stringify(message));
404
+ multi.set(getMessageIndexKey(message.id), message.threadId);
405
+ multi.zAdd(getThreadMessagesKey(message.threadId), {
406
+ score,
407
+ value: message.id
408
+ });
409
+ }
410
+ if (i === 0 && existingThread) {
411
+ const updatedThread = {
412
+ ...existingThread,
413
+ updatedAt: /* @__PURE__ */ new Date()
414
+ };
415
+ multi.set(threadKey, JSON.stringify(processRecord(_mastra_core_storage.TABLE_THREADS, updatedThread).processedRecord));
416
+ }
417
+ await multi.exec();
418
+ }
419
+ return { messages: new _mastra_core_agent.MessageList().add(messages, "memory").get.all.db() };
420
+ } catch (error) {
421
+ throw new _mastra_core_error.MastraError({
422
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "SAVE_MESSAGES", "FAILED"),
423
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
424
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
425
+ details: { threadId }
426
+ }, error);
427
+ }
428
+ }
429
+ async getThreadIdForMessage(messageId) {
430
+ const indexedThreadId = await this.client.get(getMessageIndexKey(messageId));
431
+ if (indexedThreadId) return indexedThreadId;
432
+ const keys = await this.db.scanKeys(getMessageKey("*", messageId));
433
+ if (keys.length === 0) return null;
434
+ const messageData = await this.client.get(keys[0]);
435
+ if (!messageData) return null;
436
+ const message = JSON.parse(messageData);
437
+ if (message.threadId) await this.client.set(getMessageIndexKey(messageId), message.threadId);
438
+ return message.threadId || null;
439
+ }
440
+ async getIncludedMessages(include) {
441
+ if (!include?.length) return [];
442
+ const messageIds = /* @__PURE__ */ new Set();
443
+ const messageIdToThreadIds = {};
444
+ for (const item of include) {
445
+ const itemThreadId = await this.getThreadIdForMessage(item.id);
446
+ if (!itemThreadId) continue;
447
+ messageIds.add(item.id);
448
+ messageIdToThreadIds[item.id] = itemThreadId;
449
+ const itemThreadMessagesKey = getThreadMessagesKey(itemThreadId);
450
+ const rank = await this.client.zRank(itemThreadMessagesKey, item.id);
451
+ if (rank === null) continue;
452
+ if (item.withPreviousMessages) {
453
+ const start = Math.max(0, rank - item.withPreviousMessages);
454
+ (rank === 0 ? [] : await this.client.zRange(itemThreadMessagesKey, start, rank - 1)).forEach((id) => {
455
+ messageIds.add(id);
456
+ messageIdToThreadIds[id] = itemThreadId;
457
+ });
458
+ }
459
+ if (item.withNextMessages) (await this.client.zRange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages)).forEach((id) => {
460
+ messageIds.add(id);
461
+ messageIdToThreadIds[id] = itemThreadId;
462
+ });
463
+ }
464
+ if (messageIds.size === 0) return [];
465
+ const keysToFetch = Array.from(messageIds).map((id) => getMessageKey(messageIdToThreadIds[id], id));
466
+ return (await this.client.mGet(keysToFetch)).filter((data) => data !== null).map((data) => JSON.parse(data));
467
+ }
468
+ parseStoredMessage(storedMessage) {
469
+ const defaultMessageContent = {
470
+ format: 2,
471
+ parts: [{
472
+ type: "text",
473
+ text: ""
474
+ }]
475
+ };
476
+ const { _index, ...rest } = storedMessage;
477
+ return {
478
+ ...rest,
479
+ createdAt: new Date(rest.createdAt),
480
+ content: rest.content || defaultMessageContent
481
+ };
482
+ }
483
+ async listMessagesById({ messageIds }) {
484
+ if (messageIds.length === 0) return { messages: [] };
485
+ try {
486
+ const rawMessages = [];
487
+ const indexKeys = messageIds.map((id) => getMessageIndexKey(id));
488
+ const indexResults = await this.client.mGet(indexKeys);
489
+ const indexedIds = [];
490
+ const unindexedIds = [];
491
+ messageIds.forEach((id, i) => {
492
+ const threadId = indexResults[i];
493
+ if (threadId) {
494
+ indexedIds.push({
495
+ messageId: id,
496
+ threadId
497
+ });
498
+ return;
499
+ }
500
+ unindexedIds.push(id);
501
+ });
502
+ if (indexedIds.length > 0) {
503
+ const messageKeys = indexedIds.map(({ messageId, threadId }) => getMessageKey(threadId, messageId));
504
+ const messageResults = await this.client.mGet(messageKeys);
505
+ for (const data of messageResults) if (data) rawMessages.push(JSON.parse(data));
506
+ }
507
+ if (unindexedIds.length > 0) {
508
+ const threadKeys = await this.db.scanKeys("thread:*:messages");
509
+ const foundMessages = (await Promise.all(threadKeys.map(async (threadKey) => {
510
+ const threadId = threadKey.split(":")[1];
511
+ if (!threadId) throw new Error(`Failed to parse thread ID from thread key "${threadKey}"`);
512
+ const msgKeys = unindexedIds.map((id) => getMessageKey(threadId, id));
513
+ return this.client.mGet(msgKeys);
514
+ }))).flat(1).filter((data) => !!data).map((data) => JSON.parse(data));
515
+ rawMessages.push(...foundMessages);
516
+ if (foundMessages.length > 0) {
517
+ const multi = this.client.multi();
518
+ foundMessages.forEach((msg) => {
519
+ if (msg.threadId) multi.set(getMessageIndexKey(msg.id), msg.threadId);
520
+ });
521
+ await multi.exec();
522
+ }
523
+ }
524
+ return { messages: new _mastra_core_agent.MessageList().add(rawMessages.map(this.parseStoredMessage), "memory").get.all.db() };
525
+ } catch (error) {
526
+ throw new _mastra_core_error.MastraError({
527
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_MESSAGES_BY_ID", "FAILED"),
528
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
529
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
530
+ details: { messageIds: JSON.stringify(messageIds) }
531
+ }, error);
532
+ }
533
+ }
534
+ async listMessages(args) {
535
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
536
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
537
+ const threadIdsSet = new Set(threadIds);
538
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) throw new _mastra_core_error.MastraError({
539
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_MESSAGES", "INVALID_THREAD_ID"),
540
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
541
+ category: _mastra_core_error.ErrorCategory.USER,
542
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
543
+ }, /* @__PURE__ */ new Error("threadId must be a non-empty string or array of non-empty strings"));
544
+ const perPage = (0, _mastra_core_storage.normalizePerPage)(perPageInput, 40);
545
+ const { offset, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
546
+ const metadataFilter = (0, _mastra_core_storage.validateStorageMetadataFilter)(filter?.metadata);
547
+ try {
548
+ if (page < 0) throw new _mastra_core_error.MastraError({
549
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_MESSAGES", "INVALID_PAGE"),
550
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
551
+ category: _mastra_core_error.ErrorCategory.USER,
552
+ details: { page }
553
+ }, /* @__PURE__ */ new Error("page must be >= 0"));
554
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
555
+ const getFieldValue = (msg) => {
556
+ if (field === "createdAt") return new Date(msg.createdAt).getTime();
557
+ const value = msg[field];
558
+ if (typeof value === "number") return value;
559
+ if (value instanceof Date) return value.getTime();
560
+ return 0;
561
+ };
562
+ if (perPage === 0 && (!include || include.length === 0)) return {
563
+ messages: [],
564
+ total: 0,
565
+ page,
566
+ perPage: perPageForResponse,
567
+ hasMore: false
568
+ };
569
+ let includedMessages = [];
570
+ if (include && include.length > 0) includedMessages = (await this.getIncludedMessages(include)).map(this.parseStoredMessage);
571
+ if (perPage === 0 && include && include.length > 0) return {
572
+ messages: new _mastra_core_agent.MessageList().add(includedMessages, "memory").get.all.db().sort((a, b) => {
573
+ const aValue = getFieldValue(a);
574
+ const bValue = getFieldValue(b);
575
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
576
+ }),
577
+ total: 0,
578
+ page,
579
+ perPage: perPageForResponse,
580
+ hasMore: false
581
+ };
582
+ const allMessageIdsWithThreads = [];
583
+ for (const tid of threadIds) {
584
+ const threadMessagesKey = getThreadMessagesKey(tid);
585
+ const msgIds = await this.client.zRange(threadMessagesKey, 0, -1);
586
+ for (const mid of msgIds) allMessageIdsWithThreads.push({
587
+ threadId: tid,
588
+ messageId: mid
589
+ });
590
+ }
591
+ if (allMessageIdsWithThreads.length === 0) return {
592
+ messages: [],
593
+ total: 0,
594
+ page,
595
+ perPage: perPageForResponse,
596
+ hasMore: false
597
+ };
598
+ const messageKeys = allMessageIdsWithThreads.map(({ threadId: tid, messageId }) => getMessageKey(tid, messageId));
599
+ let messagesData = (await this.client.mGet(messageKeys)).filter((data) => data !== null).map((data) => JSON.parse(data)).map(this.parseStoredMessage);
600
+ if (resourceId) messagesData = messagesData.filter((msg) => msg.resourceId === resourceId);
601
+ messagesData = (0, _mastra_core_storage.filterByDateRange)(messagesData, (msg) => new Date(msg.createdAt), filter?.dateRange);
602
+ messagesData = messagesData.filter((message) => (0, _mastra_core_storage.storageMessageMatchesMetadataFilter)(message.content, metadataFilter));
603
+ messagesData.sort((a, b) => {
604
+ const aValue = getFieldValue(a);
605
+ const bValue = getFieldValue(b);
606
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
607
+ });
608
+ const total = messagesData.length;
609
+ const start = offset;
610
+ const end = perPageInput === false ? total : start + perPage;
611
+ const paginatedMessages = messagesData.slice(start, end);
612
+ const messageIdsSet = /* @__PURE__ */ new Set();
613
+ const allMessages = [];
614
+ for (const msg of paginatedMessages) {
615
+ if (messageIdsSet.has(msg.id)) continue;
616
+ allMessages.push(msg);
617
+ messageIdsSet.add(msg.id);
618
+ }
619
+ for (const msg of includedMessages) {
620
+ if (messageIdsSet.has(msg.id)) continue;
621
+ allMessages.push(msg);
622
+ messageIdsSet.add(msg.id);
623
+ }
624
+ let finalMessages = new _mastra_core_agent.MessageList().add(allMessages, "memory").get.all.db();
625
+ finalMessages = finalMessages.sort((a, b) => {
626
+ const aValue = getFieldValue(a);
627
+ const bValue = getFieldValue(b);
628
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
629
+ });
630
+ const returnedThreadMessageIds = new Set(finalMessages.filter((message) => message.threadId && threadIdsSet.has(message.threadId)).map((message) => message.id));
631
+ const hasMore = perPageInput !== false && (metadataFilter || returnedThreadMessageIds.size < total) && offset + paginatedMessages.length < total;
632
+ return {
633
+ messages: finalMessages,
634
+ total,
635
+ page,
636
+ perPage: perPageForResponse,
637
+ hasMore
638
+ };
639
+ } catch (error) {
640
+ const mastraError = new _mastra_core_error.MastraError({
641
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_MESSAGES", "FAILED"),
642
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
643
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
644
+ details: {
645
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
646
+ resourceId: resourceId ?? ""
647
+ }
648
+ }, error);
649
+ this.logger.error(mastraError.toString());
650
+ this.logger.trackException(mastraError);
651
+ return {
652
+ messages: [],
653
+ total: 0,
654
+ page,
655
+ perPage: perPageForResponse,
656
+ hasMore: false
657
+ };
658
+ }
659
+ }
660
+ async getResourceById({ resourceId }) {
661
+ try {
662
+ const key = `${_mastra_core_storage.TABLE_RESOURCES}:${resourceId}`;
663
+ const data = await this.client.get(key);
664
+ if (!data) return null;
665
+ const resource = JSON.parse(data);
666
+ return {
667
+ ...resource,
668
+ createdAt: new Date(resource.createdAt),
669
+ updatedAt: new Date(resource.updatedAt),
670
+ workingMemory: typeof resource.workingMemory === "object" ? JSON.stringify(resource.workingMemory) : resource.workingMemory,
671
+ metadata: typeof resource.metadata === "string" ? JSON.parse(resource.metadata) : resource.metadata
672
+ };
673
+ } catch (error) {
674
+ this.logger.error("Error getting resource by ID:", error);
675
+ throw error;
676
+ }
677
+ }
678
+ async saveResource({ resource }) {
679
+ try {
680
+ const key = `${_mastra_core_storage.TABLE_RESOURCES}:${resource.id}`;
681
+ const serializedResource = {
682
+ ...resource,
683
+ metadata: JSON.stringify(resource.metadata),
684
+ createdAt: resource.createdAt.toISOString(),
685
+ updatedAt: resource.updatedAt.toISOString()
686
+ };
687
+ await this.client.set(key, JSON.stringify(serializedResource));
688
+ return resource;
689
+ } catch (error) {
690
+ this.logger.error("Error saving resource:", error);
691
+ throw error;
692
+ }
693
+ }
694
+ async updateResource({ resourceId, workingMemory, metadata }) {
695
+ try {
696
+ const existingResource = await this.getResourceById({ resourceId });
697
+ if (!existingResource) {
698
+ const newResource = {
699
+ id: resourceId,
700
+ workingMemory,
701
+ metadata: metadata || {},
702
+ createdAt: /* @__PURE__ */ new Date(),
703
+ updatedAt: /* @__PURE__ */ new Date()
704
+ };
705
+ return this.saveResource({ resource: newResource });
706
+ }
707
+ const updatedResource = {
708
+ ...existingResource,
709
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
710
+ metadata: {
711
+ ...existingResource.metadata,
712
+ ...metadata
713
+ },
714
+ updatedAt: /* @__PURE__ */ new Date()
715
+ };
716
+ await this.saveResource({ resource: updatedResource });
717
+ return updatedResource;
718
+ } catch (error) {
719
+ this.logger.error("Error updating resource:", error);
720
+ throw error;
721
+ }
722
+ }
723
+ async updateMessages(args) {
724
+ const { messages } = args;
725
+ if (messages.length === 0) return [];
726
+ try {
727
+ const messageIds = messages.map((m) => m.id);
728
+ const existingMessages = [];
729
+ const messageIdToKey = {};
730
+ for (const messageId of messageIds) {
731
+ const pattern = getMessageKey("*", messageId);
732
+ const keys = await this.db.scanKeys(pattern);
733
+ for (const key of keys) {
734
+ const data = await this.client.get(key);
735
+ if (!data) continue;
736
+ const message = JSON.parse(data);
737
+ if (message && message.id === messageId) {
738
+ existingMessages.push(message);
739
+ messageIdToKey[messageId] = key;
740
+ break;
741
+ }
742
+ }
743
+ }
744
+ if (existingMessages.length === 0) return [];
745
+ const threadIdsToUpdate = /* @__PURE__ */ new Set();
746
+ const multi = this.client.multi();
747
+ for (const existingMessage of existingMessages) {
748
+ const updatePayload = messages.find((m) => m.id === existingMessage.id);
749
+ if (!updatePayload) continue;
750
+ const { id, ...fieldsToUpdate } = updatePayload;
751
+ if (Object.keys(fieldsToUpdate).length === 0) continue;
752
+ threadIdsToUpdate.add(existingMessage.threadId);
753
+ if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) threadIdsToUpdate.add(updatePayload.threadId);
754
+ const updatedMessage = { ...existingMessage };
755
+ if (fieldsToUpdate.content) {
756
+ const existingContent = existingMessage.content;
757
+ updatedMessage.content = {
758
+ ...existingContent,
759
+ ...fieldsToUpdate.content,
760
+ ...existingContent?.metadata && fieldsToUpdate.content.metadata ? { metadata: {
761
+ ...existingContent.metadata,
762
+ ...fieldsToUpdate.content.metadata
763
+ } } : {}
764
+ };
765
+ }
766
+ for (const key in fieldsToUpdate) if (Object.prototype.hasOwnProperty.call(fieldsToUpdate, key) && key !== "content") updatedMessage[key] = fieldsToUpdate[key];
767
+ const key = messageIdToKey[id];
768
+ if (!key) continue;
769
+ if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
770
+ multi.zRem(getThreadMessagesKey(existingMessage.threadId), id);
771
+ multi.del(key);
772
+ const newKey = getMessageKey(updatePayload.threadId, id);
773
+ multi.set(newKey, JSON.stringify(updatedMessage));
774
+ multi.set(getMessageIndexKey(id), updatePayload.threadId);
775
+ const score = getMessageScore(updatedMessage);
776
+ multi.zAdd(getThreadMessagesKey(updatePayload.threadId), {
777
+ score,
778
+ value: id
779
+ });
780
+ messageIdToKey[id] = newKey;
781
+ continue;
782
+ }
783
+ multi.set(key, JSON.stringify(updatedMessage));
784
+ }
785
+ const now = /* @__PURE__ */ new Date();
786
+ for (const threadId of threadIdsToUpdate) if (threadId) {
787
+ const threadKey = getKey(_mastra_core_storage.TABLE_THREADS, { id: threadId });
788
+ const existingThreadData = await this.client.get(threadKey);
789
+ if (existingThreadData) {
790
+ const updatedThread = {
791
+ ...JSON.parse(existingThreadData),
792
+ updatedAt: now
793
+ };
794
+ multi.set(threadKey, JSON.stringify(processRecord(_mastra_core_storage.TABLE_THREADS, updatedThread).processedRecord));
795
+ }
796
+ }
797
+ await multi.exec();
798
+ const updatedMessages = [];
799
+ for (const messageId of messageIds) {
800
+ const key = messageIdToKey[messageId];
801
+ if (key) {
802
+ const data = await this.client.get(key);
803
+ if (data) updatedMessages.push(JSON.parse(data));
804
+ }
805
+ }
806
+ return updatedMessages;
807
+ } catch (error) {
808
+ throw new _mastra_core_error.MastraError({
809
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "UPDATE_MESSAGES", "FAILED"),
810
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
811
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
812
+ details: { messageIds: messages.map((m) => m.id).join(",") }
813
+ }, error);
814
+ }
815
+ }
816
+ async deleteMessages(messageIds) {
817
+ if (!messageIds || messageIds.length === 0) return;
818
+ try {
819
+ const threadIds = /* @__PURE__ */ new Set();
820
+ const messageKeys = [];
821
+ const foundMessageIds = [];
822
+ const messageIdToThreadId = /* @__PURE__ */ new Map();
823
+ const indexKeys = messageIds.map((id) => getMessageIndexKey(id));
824
+ const indexResults = await this.client.mGet(indexKeys);
825
+ const indexedMessages = [];
826
+ const unindexedMessageIds = [];
827
+ messageIds.forEach((id, i) => {
828
+ const threadId = indexResults[i];
829
+ if (threadId) {
830
+ indexedMessages.push({
831
+ messageId: id,
832
+ threadId
833
+ });
834
+ return;
835
+ }
836
+ unindexedMessageIds.push(id);
837
+ });
838
+ for (const { messageId, threadId } of indexedMessages) {
839
+ messageKeys.push(getMessageKey(threadId, messageId));
840
+ foundMessageIds.push(messageId);
841
+ messageIdToThreadId.set(messageId, threadId);
842
+ threadIds.add(threadId);
843
+ }
844
+ for (const messageId of unindexedMessageIds) {
845
+ const pattern = getMessageKey("*", messageId);
846
+ const keys = await this.db.scanKeys(pattern);
847
+ for (const key of keys) {
848
+ const data = await this.client.get(key);
849
+ if (!data) continue;
850
+ const message = JSON.parse(data);
851
+ if (message && message.id === messageId) {
852
+ messageKeys.push(key);
853
+ foundMessageIds.push(messageId);
854
+ if (message.threadId) {
855
+ messageIdToThreadId.set(messageId, message.threadId);
856
+ threadIds.add(message.threadId);
857
+ }
858
+ break;
859
+ }
860
+ }
861
+ }
862
+ if (messageKeys.length === 0) return;
863
+ const multi = this.client.multi();
864
+ for (const key of messageKeys) multi.del(key);
865
+ for (const messageId of foundMessageIds) multi.del(getMessageIndexKey(messageId));
866
+ if (threadIds.size > 0) for (const threadId of threadIds) {
867
+ for (const [msgId, msgThreadId] of messageIdToThreadId) if (msgThreadId === threadId) multi.zRem(getThreadMessagesKey(threadId), msgId);
868
+ const threadKey = getKey(_mastra_core_storage.TABLE_THREADS, { id: threadId });
869
+ const threadData = await this.client.get(threadKey);
870
+ if (!threadData) continue;
871
+ const updatedThread = {
872
+ ...JSON.parse(threadData),
873
+ updatedAt: /* @__PURE__ */ new Date()
874
+ };
875
+ multi.set(threadKey, JSON.stringify(processRecord(_mastra_core_storage.TABLE_THREADS, updatedThread).processedRecord));
876
+ }
877
+ await multi.exec();
878
+ } catch (error) {
879
+ throw new _mastra_core_error.MastraError({
880
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "DELETE_MESSAGES", "FAILED"),
881
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
882
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
883
+ details: { messageIds: messageIds.join(", ") }
884
+ }, error);
885
+ }
886
+ }
887
+ sortThreads(threads, field, direction) {
888
+ return threads.sort((a, b) => {
889
+ const aValue = new Date(a[field]).getTime();
890
+ const bValue = new Date(b[field]).getTime();
891
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
892
+ });
893
+ }
894
+ async cloneThread(args) {
895
+ const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args;
896
+ const sourceThread = await this.getThreadById({ threadId: sourceThreadId });
897
+ if (!sourceThread) throw new _mastra_core_error.MastraError({
898
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "CLONE_THREAD", "SOURCE_NOT_FOUND"),
899
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
900
+ category: _mastra_core_error.ErrorCategory.USER,
901
+ text: `Source thread with id ${sourceThreadId} not found`,
902
+ details: { sourceThreadId }
903
+ });
904
+ const newThreadId = providedThreadId || crypto.randomUUID();
905
+ if (await this.getThreadById({ threadId: newThreadId })) throw new _mastra_core_error.MastraError({
906
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "CLONE_THREAD", "THREAD_EXISTS"),
907
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
908
+ category: _mastra_core_error.ErrorCategory.USER,
909
+ text: `Thread with id ${newThreadId} already exists`,
910
+ details: { newThreadId }
911
+ });
912
+ try {
913
+ const threadMessagesKey = getThreadMessagesKey(sourceThreadId);
914
+ const messageKeys = (await this.client.zRange(threadMessagesKey, 0, -1)).map((mid) => getMessageKey(sourceThreadId, mid));
915
+ let sourceMessages = [];
916
+ if (messageKeys.length > 0) sourceMessages = (await this.client.mGet(messageKeys)).filter((data) => data !== null).map((data) => {
917
+ const msg = JSON.parse(data);
918
+ return {
919
+ ...msg,
920
+ createdAt: new Date(msg.createdAt)
921
+ };
922
+ });
923
+ if (options?.messageFilter?.startDate || options?.messageFilter?.endDate) sourceMessages = (0, _mastra_core_storage.filterByDateRange)(sourceMessages, (msg) => new Date(msg.createdAt), {
924
+ start: options.messageFilter?.startDate,
925
+ end: options.messageFilter?.endDate
926
+ });
927
+ if (options?.messageFilter?.messageIds && options.messageFilter.messageIds.length > 0) {
928
+ const messageIdSet = new Set(options.messageFilter.messageIds);
929
+ sourceMessages = sourceMessages.filter((msg) => messageIdSet.has(msg.id));
930
+ }
931
+ sourceMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
932
+ if (options?.messageLimit && options.messageLimit > 0 && sourceMessages.length > options.messageLimit) sourceMessages = sourceMessages.slice(-options.messageLimit);
933
+ const now = /* @__PURE__ */ new Date();
934
+ const lastMessageId = sourceMessages.length > 0 ? sourceMessages[sourceMessages.length - 1].id : void 0;
935
+ const cloneMetadata = {
936
+ sourceThreadId,
937
+ clonedAt: now,
938
+ ...lastMessageId && { lastMessageId }
939
+ };
940
+ const newThread = {
941
+ id: newThreadId,
942
+ resourceId: resourceId || sourceThread.resourceId,
943
+ title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : void 0),
944
+ metadata: {
945
+ ...metadata,
946
+ clone: cloneMetadata
947
+ },
948
+ createdAt: now,
949
+ updatedAt: now
950
+ };
951
+ const multi = this.client.multi();
952
+ const threadKey = getKey(_mastra_core_storage.TABLE_THREADS, { id: newThreadId });
953
+ multi.set(threadKey, JSON.stringify(processRecord(_mastra_core_storage.TABLE_THREADS, newThread).processedRecord));
954
+ const clonedMessages = [];
955
+ const targetResourceId = resourceId || sourceThread.resourceId;
956
+ const newThreadMessagesKey = getThreadMessagesKey(newThreadId);
957
+ for (let i = 0; i < sourceMessages.length; i++) {
958
+ const sourceMsg = sourceMessages[i];
959
+ const newMessageId = crypto.randomUUID();
960
+ const { _index, ...restMsg } = sourceMsg;
961
+ const newMessage = {
962
+ ...restMsg,
963
+ id: newMessageId,
964
+ threadId: newThreadId,
965
+ resourceId: targetResourceId
966
+ };
967
+ const messageKey = getMessageKey(newThreadId, newMessageId);
968
+ multi.set(messageKey, JSON.stringify(newMessage));
969
+ multi.set(getMessageIndexKey(newMessageId), newThreadId);
970
+ const score = getMessageScore({
971
+ createdAt: newMessage.createdAt,
972
+ _index: i
973
+ });
974
+ multi.zAdd(newThreadMessagesKey, {
975
+ score,
976
+ value: newMessageId
977
+ });
978
+ clonedMessages.push(newMessage);
979
+ }
980
+ await multi.exec();
981
+ return {
982
+ thread: newThread,
983
+ clonedMessages
984
+ };
985
+ } catch (error) {
986
+ if (error instanceof _mastra_core_error.MastraError) throw error;
987
+ throw new _mastra_core_error.MastraError({
988
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "CLONE_THREAD", "FAILED"),
989
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
990
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
991
+ details: {
992
+ sourceThreadId,
993
+ newThreadId
994
+ }
995
+ }, error);
996
+ }
997
+ }
1181
998
  };
1182
999
  function getThreadMessagesKey(threadId) {
1183
- return `thread:${threadId}:messages`;
1000
+ return `thread:${threadId}:messages`;
1184
1001
  }
1185
1002
  function getMessageKey(threadId, messageId) {
1186
- return getKey(storage.TABLE_MESSAGES, { threadId, id: messageId });
1003
+ return getKey(_mastra_core_storage.TABLE_MESSAGES, {
1004
+ threadId,
1005
+ id: messageId
1006
+ });
1187
1007
  }
1188
1008
  function getMessageIndexKey(messageId) {
1189
- return `msg-idx:${messageId}`;
1009
+ return `msg-idx:${messageId}`;
1190
1010
  }
1191
1011
  function getMessageScore(message) {
1192
- const createdAtScore = new Date(message.createdAt).getTime();
1193
- const index = typeof message._index === "number" ? message._index : 0;
1194
- return createdAtScore * 1e3 + index;
1012
+ const createdAtScore = new Date(message.createdAt).getTime();
1013
+ const index = typeof message._index === "number" ? message._index : 0;
1014
+ return createdAtScore * 1e3 + index;
1195
1015
  }
1016
+ //#endregion
1017
+ //#region src/storage/domains/scores/index.ts
1018
+ /** Returns true when a row matches the multi-tenant scope filters (or none provided). */
1196
1019
  function matchesTenancy(row, filters) {
1197
- if (filters?.organizationId !== void 0 && row.organizationId !== filters.organizationId) return false;
1198
- if (filters?.projectId !== void 0 && row.projectId !== filters.projectId) return false;
1199
- return true;
1020
+ if (filters?.organizationId !== void 0 && row.organizationId !== filters.organizationId) return false;
1021
+ if (filters?.projectId !== void 0 && row.projectId !== filters.projectId) return false;
1022
+ return true;
1200
1023
  }
1201
- var ScoresRedis = class extends storage.ScoresStorage {
1202
- client;
1203
- db;
1204
- constructor(config) {
1205
- super();
1206
- this.client = config.client;
1207
- this.db = new RedisDB({ client: config.client });
1208
- }
1209
- async dangerouslyClearAll() {
1210
- await this.db.deleteData({ tableName: storage.TABLE_SCORERS });
1211
- }
1212
- async getScoreById({ id }) {
1213
- try {
1214
- const data = await this.db.get({
1215
- tableName: storage.TABLE_SCORERS,
1216
- keys: { id }
1217
- });
1218
- if (!data) {
1219
- return null;
1220
- }
1221
- return storage.transformScoreRow(data);
1222
- } catch (error$1) {
1223
- throw new error.MastraError(
1224
- {
1225
- id: storage.createStorageErrorId("REDIS", "GET_SCORE_BY_ID", "FAILED"),
1226
- domain: error.ErrorDomain.STORAGE,
1227
- category: error.ErrorCategory.THIRD_PARTY,
1228
- details: {
1229
- ...id && { id }
1230
- }
1231
- },
1232
- error$1
1233
- );
1234
- }
1235
- }
1236
- async listScoresByScorerId({
1237
- scorerId,
1238
- entityId,
1239
- entityType,
1240
- source,
1241
- pagination = { page: 0, perPage: 20 },
1242
- filters
1243
- }) {
1244
- return this.fetchAndFilterScores(pagination, (row) => {
1245
- if (row.scorerId !== scorerId) {
1246
- return false;
1247
- }
1248
- if (entityId && row.entityId !== entityId) {
1249
- return false;
1250
- }
1251
- if (entityType && row.entityType !== entityType) {
1252
- return false;
1253
- }
1254
- if (source && row.source !== source) {
1255
- return false;
1256
- }
1257
- if (!matchesTenancy(row, filters)) {
1258
- return false;
1259
- }
1260
- return true;
1261
- });
1262
- }
1263
- async saveScore(score) {
1264
- let validatedScore;
1265
- try {
1266
- validatedScore = evals.saveScorePayloadSchema.parse(score);
1267
- } catch (error$1) {
1268
- throw new error.MastraError(
1269
- {
1270
- id: storage.createStorageErrorId("REDIS", "SAVE_SCORE", "VALIDATION_FAILED"),
1271
- domain: error.ErrorDomain.STORAGE,
1272
- category: error.ErrorCategory.USER,
1273
- details: {
1274
- scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
1275
- entityId: score.entityId ?? "unknown",
1276
- entityType: score.entityType ?? "unknown",
1277
- traceId: score.traceId ?? "",
1278
- spanId: score.spanId ?? ""
1279
- }
1280
- },
1281
- error$1
1282
- );
1283
- }
1284
- const now = /* @__PURE__ */ new Date();
1285
- const id = crypto2__default.default.randomUUID();
1286
- const scoreWithId = {
1287
- ...validatedScore,
1288
- id,
1289
- createdAt: now,
1290
- updatedAt: now
1291
- };
1292
- const { key, processedRecord } = processRecord(storage.TABLE_SCORERS, scoreWithId);
1293
- try {
1294
- await this.client.set(key, JSON.stringify(processedRecord));
1295
- return { score: { ...validatedScore, id, createdAt: now, updatedAt: now } };
1296
- } catch (error$1) {
1297
- throw new error.MastraError(
1298
- {
1299
- id: storage.createStorageErrorId("REDIS", "SAVE_SCORE", "FAILED"),
1300
- domain: error.ErrorDomain.STORAGE,
1301
- category: error.ErrorCategory.THIRD_PARTY,
1302
- details: { id }
1303
- },
1304
- error$1
1305
- );
1306
- }
1307
- }
1308
- async listScoresByRunId({
1309
- runId,
1310
- pagination = { page: 0, perPage: 20 },
1311
- filters
1312
- }) {
1313
- return this.fetchAndFilterScores(pagination, (row) => row.runId === runId && matchesTenancy(row, filters));
1314
- }
1315
- async listScoresByEntityId({
1316
- entityId,
1317
- entityType,
1318
- pagination = { page: 0, perPage: 20 },
1319
- filters
1320
- }) {
1321
- return this.fetchAndFilterScores(pagination, (row) => {
1322
- if (row.entityId !== entityId) {
1323
- return false;
1324
- }
1325
- if (entityType && row.entityType !== entityType) {
1326
- return false;
1327
- }
1328
- if (!matchesTenancy(row, filters)) {
1329
- return false;
1330
- }
1331
- return true;
1332
- });
1333
- }
1334
- async listScoresBySpan({
1335
- traceId,
1336
- spanId,
1337
- pagination = { page: 0, perPage: 20 },
1338
- filters
1339
- }) {
1340
- return this.fetchAndFilterScores(
1341
- pagination,
1342
- (row) => row.traceId === traceId && row.spanId === spanId && matchesTenancy(row, filters)
1343
- );
1344
- }
1345
- async fetchAndFilterScores(pagination, filterFn) {
1346
- const { page, perPage: perPageInput } = pagination;
1347
- const keys = await this.db.scanKeys(`${storage.TABLE_SCORERS}:*`);
1348
- if (keys.length === 0) {
1349
- return {
1350
- scores: [],
1351
- pagination: { total: 0, page, perPage: perPageInput, hasMore: false }
1352
- };
1353
- }
1354
- const results = await this.client.mGet(keys);
1355
- const filtered = results.map((data) => {
1356
- if (!data) {
1357
- return null;
1358
- }
1359
- try {
1360
- return JSON.parse(data);
1361
- } catch {
1362
- return null;
1363
- }
1364
- }).filter((row) => !!row && typeof row === "object" && filterFn(row));
1365
- const total = filtered.length;
1366
- const perPage = storage.normalizePerPage(perPageInput, 100);
1367
- const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1368
- const end = perPageInput === false ? total : start + perPage;
1369
- const scores = filtered.slice(start, end).map((row) => storage.transformScoreRow(row));
1370
- return {
1371
- scores,
1372
- pagination: {
1373
- total,
1374
- page,
1375
- perPage: perPageForResponse,
1376
- hasMore: end < total
1377
- }
1378
- };
1379
- }
1024
+ var ScoresRedis = class extends _mastra_core_storage.ScoresStorage {
1025
+ client;
1026
+ db;
1027
+ constructor(config) {
1028
+ super();
1029
+ this.client = config.client;
1030
+ this.db = new RedisDB({ client: config.client });
1031
+ }
1032
+ async dangerouslyClearAll() {
1033
+ await this.db.deleteData({ tableName: _mastra_core_storage.TABLE_SCORERS });
1034
+ }
1035
+ async getScoreById({ id }) {
1036
+ try {
1037
+ const data = await this.db.get({
1038
+ tableName: _mastra_core_storage.TABLE_SCORERS,
1039
+ keys: { id }
1040
+ });
1041
+ if (!data) return null;
1042
+ return (0, _mastra_core_storage.transformScoreRow)(data);
1043
+ } catch (error) {
1044
+ throw new _mastra_core_error.MastraError({
1045
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "GET_SCORE_BY_ID", "FAILED"),
1046
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1047
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1048
+ details: { ...id && { id } }
1049
+ }, error);
1050
+ }
1051
+ }
1052
+ async listScoresByScorerId({ scorerId, entityId, entityType, source, pagination = {
1053
+ page: 0,
1054
+ perPage: 20
1055
+ }, filters }) {
1056
+ return this.fetchAndFilterScores(pagination, (row) => {
1057
+ if (row.scorerId !== scorerId) return false;
1058
+ if (entityId && row.entityId !== entityId) return false;
1059
+ if (entityType && row.entityType !== entityType) return false;
1060
+ if (source && row.source !== source) return false;
1061
+ if (!matchesTenancy(row, filters)) return false;
1062
+ return true;
1063
+ });
1064
+ }
1065
+ async saveScore(score) {
1066
+ let validatedScore;
1067
+ try {
1068
+ validatedScore = _mastra_core_evals.saveScorePayloadSchema.parse(score);
1069
+ } catch (error) {
1070
+ throw new _mastra_core_error.MastraError({
1071
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "SAVE_SCORE", "VALIDATION_FAILED"),
1072
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1073
+ category: _mastra_core_error.ErrorCategory.USER,
1074
+ details: {
1075
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
1076
+ entityId: score.entityId ?? "unknown",
1077
+ entityType: score.entityType ?? "unknown",
1078
+ traceId: score.traceId ?? "",
1079
+ spanId: score.spanId ?? ""
1080
+ }
1081
+ }, error);
1082
+ }
1083
+ const now = /* @__PURE__ */ new Date();
1084
+ const id = crypto$1.default.randomUUID();
1085
+ const { key, processedRecord } = processRecord(_mastra_core_storage.TABLE_SCORERS, {
1086
+ ...validatedScore,
1087
+ id,
1088
+ createdAt: now,
1089
+ updatedAt: now
1090
+ });
1091
+ try {
1092
+ await this.client.set(key, JSON.stringify(processedRecord));
1093
+ return { score: {
1094
+ ...validatedScore,
1095
+ id,
1096
+ createdAt: now,
1097
+ updatedAt: now
1098
+ } };
1099
+ } catch (error) {
1100
+ throw new _mastra_core_error.MastraError({
1101
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "SAVE_SCORE", "FAILED"),
1102
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1103
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1104
+ details: { id }
1105
+ }, error);
1106
+ }
1107
+ }
1108
+ async listScoresByRunId({ runId, pagination = {
1109
+ page: 0,
1110
+ perPage: 20
1111
+ }, filters }) {
1112
+ return this.fetchAndFilterScores(pagination, (row) => row.runId === runId && matchesTenancy(row, filters));
1113
+ }
1114
+ async listScoresByEntityId({ entityId, entityType, pagination = {
1115
+ page: 0,
1116
+ perPage: 20
1117
+ }, filters }) {
1118
+ return this.fetchAndFilterScores(pagination, (row) => {
1119
+ if (row.entityId !== entityId) return false;
1120
+ if (entityType && row.entityType !== entityType) return false;
1121
+ if (!matchesTenancy(row, filters)) return false;
1122
+ return true;
1123
+ });
1124
+ }
1125
+ async listScoresBySpan({ traceId, spanId, pagination = {
1126
+ page: 0,
1127
+ perPage: 20
1128
+ }, filters }) {
1129
+ return this.fetchAndFilterScores(pagination, (row) => row.traceId === traceId && row.spanId === spanId && matchesTenancy(row, filters));
1130
+ }
1131
+ async fetchAndFilterScores(pagination, filterFn) {
1132
+ const { page, perPage: perPageInput } = pagination;
1133
+ const keys = await this.db.scanKeys(`${_mastra_core_storage.TABLE_SCORERS}:*`);
1134
+ if (keys.length === 0) return {
1135
+ scores: [],
1136
+ pagination: {
1137
+ total: 0,
1138
+ page,
1139
+ perPage: perPageInput,
1140
+ hasMore: false
1141
+ }
1142
+ };
1143
+ const filtered = (await this.client.mGet(keys)).map((data) => {
1144
+ if (!data) return null;
1145
+ try {
1146
+ return JSON.parse(data);
1147
+ } catch {
1148
+ return null;
1149
+ }
1150
+ }).filter((row) => !!row && typeof row === "object" && filterFn(row));
1151
+ const total = filtered.length;
1152
+ const perPage = (0, _mastra_core_storage.normalizePerPage)(perPageInput, 100);
1153
+ const { offset: start, perPage: perPageForResponse } = (0, _mastra_core_storage.calculatePagination)(page, perPageInput, perPage);
1154
+ const end = perPageInput === false ? total : start + perPage;
1155
+ return {
1156
+ scores: filtered.slice(start, end).map((row) => (0, _mastra_core_storage.transformScoreRow)(row)),
1157
+ pagination: {
1158
+ total,
1159
+ page,
1160
+ perPage: perPageForResponse,
1161
+ hasMore: end < total
1162
+ }
1163
+ };
1164
+ }
1380
1165
  };
1166
+ //#endregion
1167
+ //#region src/storage/domains/workflows/index.ts
1381
1168
  function parseWorkflowRun(row) {
1382
- let parsedSnapshot = row.snapshot;
1383
- if (typeof parsedSnapshot === "string") {
1384
- try {
1385
- parsedSnapshot = JSON.parse(row.snapshot);
1386
- } catch (e) {
1387
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1388
- }
1389
- }
1390
- return {
1391
- workflowName: row.workflow_name,
1392
- runId: row.run_id,
1393
- snapshot: parsedSnapshot,
1394
- createdAt: storage.ensureDate(row.createdAt),
1395
- updatedAt: storage.ensureDate(row.updatedAt),
1396
- resourceId: row.resourceId
1397
- };
1169
+ let parsedSnapshot = row.snapshot;
1170
+ if (typeof parsedSnapshot === "string") try {
1171
+ parsedSnapshot = JSON.parse(row.snapshot);
1172
+ } catch (e) {
1173
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1174
+ }
1175
+ return {
1176
+ workflowName: row.workflow_name,
1177
+ runId: row.run_id,
1178
+ snapshot: parsedSnapshot,
1179
+ createdAt: (0, _mastra_core_storage.ensureDate)(row.createdAt),
1180
+ updatedAt: (0, _mastra_core_storage.ensureDate)(row.updatedAt),
1181
+ resourceId: row.resourceId
1182
+ };
1398
1183
  }
1399
- var WorkflowsRedis = class extends storage.WorkflowsStorage {
1400
- client;
1401
- db;
1402
- constructor(config) {
1403
- super();
1404
- this.client = config.client;
1405
- this.db = new RedisDB({ client: config.client });
1406
- }
1407
- supportsConcurrentUpdates() {
1408
- return false;
1409
- }
1410
- async dangerouslyClearAll() {
1411
- await this.db.deleteData({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
1412
- }
1413
- async updateWorkflowResults({
1414
- workflowName,
1415
- runId,
1416
- stepId,
1417
- result,
1418
- requestContext
1419
- }) {
1420
- try {
1421
- const existingRecord = await this.db.get({
1422
- tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
1423
- keys: {
1424
- namespace: "workflows",
1425
- workflow_name: workflowName,
1426
- run_id: runId
1427
- }
1428
- });
1429
- const existingSnapshot = existingRecord?.snapshot;
1430
- let snapshot = existingSnapshot;
1431
- if (!snapshot) {
1432
- snapshot = {
1433
- context: {},
1434
- activePaths: [],
1435
- timestamp: Date.now(),
1436
- suspendedPaths: {},
1437
- activeStepsPath: {},
1438
- resumeLabels: {},
1439
- serializedStepGraph: [],
1440
- status: "pending",
1441
- value: {},
1442
- waitingPaths: {},
1443
- runId,
1444
- requestContext: {}
1445
- };
1446
- }
1447
- snapshot.context[stepId] = result;
1448
- snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
1449
- await this.persistWorkflowSnapshot({
1450
- namespace: "workflows",
1451
- workflowName,
1452
- runId,
1453
- snapshot,
1454
- createdAt: existingRecord?.createdAt ? storage.ensureDate(existingRecord.createdAt) : void 0
1455
- });
1456
- return snapshot.context;
1457
- } catch (error$1) {
1458
- if (error$1 instanceof error.MastraError) {
1459
- throw error$1;
1460
- }
1461
- throw new error.MastraError(
1462
- {
1463
- id: storage.createStorageErrorId("REDIS", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
1464
- domain: error.ErrorDomain.STORAGE,
1465
- category: error.ErrorCategory.THIRD_PARTY,
1466
- details: { workflowName, runId, stepId }
1467
- },
1468
- error$1
1469
- );
1470
- }
1471
- }
1472
- async updateWorkflowState({
1473
- workflowName,
1474
- runId,
1475
- opts
1476
- }) {
1477
- try {
1478
- const existingRecord = await this.db.get({
1479
- tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
1480
- keys: {
1481
- namespace: "workflows",
1482
- workflow_name: workflowName,
1483
- run_id: runId
1484
- }
1485
- });
1486
- const existingSnapshot = existingRecord?.snapshot;
1487
- if (!existingSnapshot || !existingSnapshot.context) {
1488
- return void 0;
1489
- }
1490
- const updatedSnapshot = { ...existingSnapshot, ...opts };
1491
- await this.persistWorkflowSnapshot({
1492
- namespace: "workflows",
1493
- workflowName,
1494
- runId,
1495
- snapshot: updatedSnapshot,
1496
- createdAt: existingRecord?.createdAt ? storage.ensureDate(existingRecord.createdAt) : void 0
1497
- });
1498
- return updatedSnapshot;
1499
- } catch (error$1) {
1500
- if (error$1 instanceof error.MastraError) {
1501
- throw error$1;
1502
- }
1503
- throw new error.MastraError(
1504
- {
1505
- id: storage.createStorageErrorId("REDIS", "UPDATE_WORKFLOW_STATE", "FAILED"),
1506
- domain: error.ErrorDomain.STORAGE,
1507
- category: error.ErrorCategory.THIRD_PARTY,
1508
- details: { workflowName, runId }
1509
- },
1510
- error$1
1511
- );
1512
- }
1513
- }
1514
- async persistWorkflowSnapshot(params) {
1515
- const { namespace = "workflows", workflowName, runId, resourceId, snapshot, createdAt, updatedAt } = params;
1516
- try {
1517
- let finalCreatedAt = createdAt;
1518
- if (!finalCreatedAt) {
1519
- const existing = await this.db.get({
1520
- tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
1521
- keys: {
1522
- namespace,
1523
- workflow_name: workflowName,
1524
- run_id: runId
1525
- }
1526
- });
1527
- finalCreatedAt = existing?.createdAt ? storage.ensureDate(existing.createdAt) : /* @__PURE__ */ new Date();
1528
- }
1529
- await this.db.insert({
1530
- tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
1531
- record: {
1532
- namespace,
1533
- workflow_name: workflowName,
1534
- run_id: runId,
1535
- resourceId,
1536
- snapshot,
1537
- createdAt: finalCreatedAt,
1538
- updatedAt: updatedAt ?? /* @__PURE__ */ new Date()
1539
- }
1540
- });
1541
- } catch (error$1) {
1542
- throw new error.MastraError(
1543
- {
1544
- id: storage.createStorageErrorId("REDIS", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
1545
- domain: error.ErrorDomain.STORAGE,
1546
- category: error.ErrorCategory.THIRD_PARTY,
1547
- details: {
1548
- namespace,
1549
- workflowName,
1550
- runId
1551
- }
1552
- },
1553
- error$1
1554
- );
1555
- }
1556
- }
1557
- async loadWorkflowSnapshot(params) {
1558
- const { namespace = "workflows", workflowName, runId } = params;
1559
- const key = getKey(storage.TABLE_WORKFLOW_SNAPSHOT, {
1560
- namespace,
1561
- workflow_name: workflowName,
1562
- run_id: runId
1563
- });
1564
- try {
1565
- const data = await this.client.get(key);
1566
- if (!data) {
1567
- return null;
1568
- }
1569
- const parsed = JSON.parse(data);
1570
- return parsed.snapshot;
1571
- } catch (error$1) {
1572
- throw new error.MastraError(
1573
- {
1574
- id: storage.createStorageErrorId("REDIS", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
1575
- domain: error.ErrorDomain.STORAGE,
1576
- category: error.ErrorCategory.THIRD_PARTY,
1577
- details: {
1578
- namespace,
1579
- workflowName,
1580
- runId
1581
- }
1582
- },
1583
- error$1
1584
- );
1585
- }
1586
- }
1587
- async getWorkflowRunById({
1588
- runId,
1589
- workflowName
1590
- }) {
1591
- try {
1592
- const key = getKey(storage.TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName, run_id: runId }) + "*";
1593
- const keys = await this.db.scanKeys(key);
1594
- if (keys.length === 0) {
1595
- return null;
1596
- }
1597
- const results = await this.client.mGet(keys);
1598
- const workflows = results.filter((data2) => data2 !== null).map(
1599
- (data2) => JSON.parse(data2)
1600
- );
1601
- const data = workflows.find((workflow) => {
1602
- if (!workflow) {
1603
- return false;
1604
- }
1605
- const runIdMatch = workflow.run_id === runId;
1606
- if (workflowName) {
1607
- return runIdMatch && workflow.workflow_name === workflowName;
1608
- }
1609
- return runIdMatch;
1610
- });
1611
- if (!data) {
1612
- return null;
1613
- }
1614
- return parseWorkflowRun(data);
1615
- } catch (error$1) {
1616
- throw new error.MastraError(
1617
- {
1618
- id: storage.createStorageErrorId("REDIS", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
1619
- domain: error.ErrorDomain.STORAGE,
1620
- category: error.ErrorCategory.THIRD_PARTY,
1621
- details: {
1622
- namespace: "workflows",
1623
- runId,
1624
- workflowName: workflowName || ""
1625
- }
1626
- },
1627
- error$1
1628
- );
1629
- }
1630
- }
1631
- async deleteWorkflowRunById({ runId, workflowName }) {
1632
- const key = getKey(storage.TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName, run_id: runId });
1633
- try {
1634
- await this.client.del(key);
1635
- } catch (error$1) {
1636
- throw new error.MastraError(
1637
- {
1638
- id: storage.createStorageErrorId("REDIS", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
1639
- domain: error.ErrorDomain.STORAGE,
1640
- category: error.ErrorCategory.THIRD_PARTY,
1641
- details: {
1642
- namespace: "workflows",
1643
- runId,
1644
- workflowName
1645
- }
1646
- },
1647
- error$1
1648
- );
1649
- }
1650
- }
1651
- async listWorkflowRuns({
1652
- workflowName,
1653
- fromDate,
1654
- toDate,
1655
- perPage,
1656
- page,
1657
- resourceId,
1658
- status
1659
- } = {}) {
1660
- try {
1661
- if (page !== void 0 && page < 0) {
1662
- throw new error.MastraError(
1663
- {
1664
- id: storage.createStorageErrorId("REDIS", "LIST_WORKFLOW_RUNS", "INVALID_PAGE"),
1665
- domain: error.ErrorDomain.STORAGE,
1666
- category: error.ErrorCategory.USER,
1667
- details: { page }
1668
- },
1669
- new Error("page must be >= 0")
1670
- );
1671
- }
1672
- const normalizedFrom = fromDate ? storage.ensureDate(fromDate) : void 0;
1673
- const normalizedTo = toDate ? storage.ensureDate(toDate) : void 0;
1674
- let pattern = getKey(storage.TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows" }) + ":*";
1675
- if (workflowName && resourceId) {
1676
- pattern = getKey(storage.TABLE_WORKFLOW_SNAPSHOT, {
1677
- namespace: "workflows",
1678
- workflow_name: workflowName,
1679
- run_id: "*",
1680
- resourceId
1681
- });
1682
- } else if (workflowName) {
1683
- pattern = getKey(storage.TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName }) + ":*";
1684
- } else if (resourceId) {
1685
- pattern = getKey(storage.TABLE_WORKFLOW_SNAPSHOT, {
1686
- namespace: "workflows",
1687
- workflow_name: "*",
1688
- run_id: "*",
1689
- resourceId
1690
- });
1691
- }
1692
- const keys = await this.db.scanKeys(pattern);
1693
- if (keys.length === 0) {
1694
- return { runs: [], total: 0 };
1695
- }
1696
- const results = await this.client.mGet(keys);
1697
- let runs = results.filter((data) => data !== null).map((data) => JSON.parse(data)).filter(
1698
- (record) => record !== null && record !== void 0 && typeof record === "object" && "workflow_name" in record
1699
- ).filter((record) => !workflowName || record.workflow_name === workflowName).map((w) => parseWorkflowRun(w)).filter((w) => {
1700
- if (normalizedFrom && w.createdAt < normalizedFrom) {
1701
- return false;
1702
- }
1703
- if (normalizedTo && w.createdAt > normalizedTo) {
1704
- return false;
1705
- }
1706
- if (status) {
1707
- let snapshot = w.snapshot;
1708
- if (typeof snapshot === "string") {
1709
- try {
1710
- snapshot = JSON.parse(snapshot);
1711
- } catch (e) {
1712
- console.warn(`Failed to parse snapshot for workflow ${w.workflowName}: ${e}`);
1713
- return false;
1714
- }
1715
- }
1716
- return snapshot.status === status;
1717
- }
1718
- return true;
1719
- }).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
1720
- const total = runs.length;
1721
- if (typeof perPage === "number" && typeof page === "number") {
1722
- const normalizedPerPage = storage.normalizePerPage(perPage, Number.MAX_SAFE_INTEGER);
1723
- const offset = page * normalizedPerPage;
1724
- runs = runs.slice(offset, offset + normalizedPerPage);
1725
- }
1726
- return { runs, total };
1727
- } catch (error$1) {
1728
- if (error$1 instanceof error.MastraError) {
1729
- throw error$1;
1730
- }
1731
- throw new error.MastraError(
1732
- {
1733
- id: storage.createStorageErrorId("REDIS", "LIST_WORKFLOW_RUNS", "FAILED"),
1734
- domain: error.ErrorDomain.STORAGE,
1735
- category: error.ErrorCategory.THIRD_PARTY,
1736
- details: {
1737
- namespace: "workflows",
1738
- workflowName: workflowName || "",
1739
- resourceId: resourceId || ""
1740
- }
1741
- },
1742
- error$1
1743
- );
1744
- }
1745
- }
1184
+ var WorkflowsRedis = class extends _mastra_core_storage.WorkflowsStorage {
1185
+ client;
1186
+ db;
1187
+ constructor(config) {
1188
+ super();
1189
+ this.client = config.client;
1190
+ this.db = new RedisDB({ client: config.client });
1191
+ }
1192
+ supportsConcurrentUpdates() {
1193
+ return false;
1194
+ }
1195
+ async dangerouslyClearAll() {
1196
+ await this.db.deleteData({ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT });
1197
+ }
1198
+ async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext }) {
1199
+ try {
1200
+ const existingRecord = await this.db.get({
1201
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
1202
+ keys: {
1203
+ namespace: "workflows",
1204
+ workflow_name: workflowName,
1205
+ run_id: runId
1206
+ }
1207
+ });
1208
+ let snapshot = existingRecord?.snapshot;
1209
+ if (!snapshot) snapshot = {
1210
+ context: {},
1211
+ activePaths: [],
1212
+ timestamp: Date.now(),
1213
+ suspendedPaths: {},
1214
+ activeStepsPath: {},
1215
+ resumeLabels: {},
1216
+ serializedStepGraph: [],
1217
+ status: "pending",
1218
+ value: {},
1219
+ waitingPaths: {},
1220
+ runId,
1221
+ requestContext: {}
1222
+ };
1223
+ snapshot.context[stepId] = result;
1224
+ snapshot.requestContext = {
1225
+ ...snapshot.requestContext,
1226
+ ...requestContext
1227
+ };
1228
+ await this.persistWorkflowSnapshot({
1229
+ namespace: "workflows",
1230
+ workflowName,
1231
+ runId,
1232
+ snapshot,
1233
+ createdAt: existingRecord?.createdAt ? (0, _mastra_core_storage.ensureDate)(existingRecord.createdAt) : void 0
1234
+ });
1235
+ return snapshot.context;
1236
+ } catch (error) {
1237
+ if (error instanceof _mastra_core_error.MastraError) throw error;
1238
+ throw new _mastra_core_error.MastraError({
1239
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
1240
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1241
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1242
+ details: {
1243
+ workflowName,
1244
+ runId,
1245
+ stepId
1246
+ }
1247
+ }, error);
1248
+ }
1249
+ }
1250
+ async updateWorkflowState({ workflowName, runId, opts }) {
1251
+ try {
1252
+ const existingRecord = await this.db.get({
1253
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
1254
+ keys: {
1255
+ namespace: "workflows",
1256
+ workflow_name: workflowName,
1257
+ run_id: runId
1258
+ }
1259
+ });
1260
+ const existingSnapshot = existingRecord?.snapshot;
1261
+ if (!existingSnapshot || !existingSnapshot.context) return;
1262
+ const updatedSnapshot = {
1263
+ ...existingSnapshot,
1264
+ ...opts
1265
+ };
1266
+ await this.persistWorkflowSnapshot({
1267
+ namespace: "workflows",
1268
+ workflowName,
1269
+ runId,
1270
+ snapshot: updatedSnapshot,
1271
+ createdAt: existingRecord?.createdAt ? (0, _mastra_core_storage.ensureDate)(existingRecord.createdAt) : void 0
1272
+ });
1273
+ return updatedSnapshot;
1274
+ } catch (error) {
1275
+ if (error instanceof _mastra_core_error.MastraError) throw error;
1276
+ throw new _mastra_core_error.MastraError({
1277
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "UPDATE_WORKFLOW_STATE", "FAILED"),
1278
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1279
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1280
+ details: {
1281
+ workflowName,
1282
+ runId
1283
+ }
1284
+ }, error);
1285
+ }
1286
+ }
1287
+ async persistWorkflowSnapshot(params) {
1288
+ const { namespace = "workflows", workflowName, runId, resourceId, snapshot, createdAt, updatedAt } = params;
1289
+ try {
1290
+ let finalCreatedAt = createdAt;
1291
+ if (!finalCreatedAt) {
1292
+ const existing = await this.db.get({
1293
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
1294
+ keys: {
1295
+ namespace,
1296
+ workflow_name: workflowName,
1297
+ run_id: runId
1298
+ }
1299
+ });
1300
+ finalCreatedAt = existing?.createdAt ? (0, _mastra_core_storage.ensureDate)(existing.createdAt) : /* @__PURE__ */ new Date();
1301
+ }
1302
+ await this.db.insert({
1303
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
1304
+ record: {
1305
+ namespace,
1306
+ workflow_name: workflowName,
1307
+ run_id: runId,
1308
+ resourceId,
1309
+ snapshot,
1310
+ createdAt: finalCreatedAt,
1311
+ updatedAt: updatedAt ?? /* @__PURE__ */ new Date()
1312
+ }
1313
+ });
1314
+ } catch (error) {
1315
+ throw new _mastra_core_error.MastraError({
1316
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
1317
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1318
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1319
+ details: {
1320
+ namespace,
1321
+ workflowName,
1322
+ runId
1323
+ }
1324
+ }, error);
1325
+ }
1326
+ }
1327
+ async loadWorkflowSnapshot(params) {
1328
+ const { namespace = "workflows", workflowName, runId } = params;
1329
+ const key = getKey(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, {
1330
+ namespace,
1331
+ workflow_name: workflowName,
1332
+ run_id: runId
1333
+ });
1334
+ try {
1335
+ const data = await this.client.get(key);
1336
+ if (!data) return null;
1337
+ return JSON.parse(data).snapshot;
1338
+ } catch (error) {
1339
+ throw new _mastra_core_error.MastraError({
1340
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
1341
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1342
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1343
+ details: {
1344
+ namespace,
1345
+ workflowName,
1346
+ runId
1347
+ }
1348
+ }, error);
1349
+ }
1350
+ }
1351
+ async getWorkflowRunById({ runId, workflowName }) {
1352
+ try {
1353
+ const key = getKey(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, {
1354
+ namespace: "workflows",
1355
+ workflow_name: workflowName,
1356
+ run_id: runId
1357
+ }) + "*";
1358
+ const keys = await this.db.scanKeys(key);
1359
+ if (keys.length === 0) return null;
1360
+ const data = (await this.client.mGet(keys)).filter((data) => data !== null).map((data) => JSON.parse(data)).find((workflow) => {
1361
+ if (!workflow) return false;
1362
+ const runIdMatch = workflow.run_id === runId;
1363
+ if (workflowName) return runIdMatch && workflow.workflow_name === workflowName;
1364
+ return runIdMatch;
1365
+ });
1366
+ if (!data) return null;
1367
+ return parseWorkflowRun(data);
1368
+ } catch (error) {
1369
+ throw new _mastra_core_error.MastraError({
1370
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
1371
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1372
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1373
+ details: {
1374
+ namespace: "workflows",
1375
+ runId,
1376
+ workflowName: workflowName || ""
1377
+ }
1378
+ }, error);
1379
+ }
1380
+ }
1381
+ async deleteWorkflowRunById({ runId, workflowName }) {
1382
+ const key = getKey(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, {
1383
+ namespace: "workflows",
1384
+ workflow_name: workflowName,
1385
+ run_id: runId
1386
+ });
1387
+ try {
1388
+ await this.client.del(key);
1389
+ } catch (error) {
1390
+ throw new _mastra_core_error.MastraError({
1391
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
1392
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1393
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1394
+ details: {
1395
+ namespace: "workflows",
1396
+ runId,
1397
+ workflowName
1398
+ }
1399
+ }, error);
1400
+ }
1401
+ }
1402
+ async listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId, status } = {}) {
1403
+ try {
1404
+ if (page !== void 0 && page < 0) throw new _mastra_core_error.MastraError({
1405
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_WORKFLOW_RUNS", "INVALID_PAGE"),
1406
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1407
+ category: _mastra_core_error.ErrorCategory.USER,
1408
+ details: { page }
1409
+ }, /* @__PURE__ */ new Error("page must be >= 0"));
1410
+ const normalizedFrom = fromDate ? (0, _mastra_core_storage.ensureDate)(fromDate) : void 0;
1411
+ const normalizedTo = toDate ? (0, _mastra_core_storage.ensureDate)(toDate) : void 0;
1412
+ let pattern = getKey(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows" }) + ":*";
1413
+ if (workflowName && resourceId) pattern = getKey(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, {
1414
+ namespace: "workflows",
1415
+ workflow_name: workflowName,
1416
+ run_id: "*",
1417
+ resourceId
1418
+ });
1419
+ else if (workflowName) pattern = getKey(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, {
1420
+ namespace: "workflows",
1421
+ workflow_name: workflowName
1422
+ }) + ":*";
1423
+ else if (resourceId) pattern = getKey(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, {
1424
+ namespace: "workflows",
1425
+ workflow_name: "*",
1426
+ run_id: "*",
1427
+ resourceId
1428
+ });
1429
+ const keys = await this.db.scanKeys(pattern);
1430
+ if (keys.length === 0) return {
1431
+ runs: [],
1432
+ total: 0
1433
+ };
1434
+ let runs = (await this.client.mGet(keys)).filter((data) => data !== null).map((data) => JSON.parse(data)).filter((record) => record !== null && record !== void 0 && typeof record === "object" && "workflow_name" in record).filter((record) => !workflowName || record.workflow_name === workflowName).map((w) => parseWorkflowRun(w)).filter((w) => {
1435
+ if (normalizedFrom && w.createdAt < normalizedFrom) return false;
1436
+ if (normalizedTo && w.createdAt > normalizedTo) return false;
1437
+ if (status) {
1438
+ let snapshot = w.snapshot;
1439
+ if (typeof snapshot === "string") try {
1440
+ snapshot = JSON.parse(snapshot);
1441
+ } catch (e) {
1442
+ console.warn(`Failed to parse snapshot for workflow ${w.workflowName}: ${e}`);
1443
+ return false;
1444
+ }
1445
+ return snapshot.status === status;
1446
+ }
1447
+ return true;
1448
+ }).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
1449
+ const total = runs.length;
1450
+ if (typeof perPage === "number" && typeof page === "number") {
1451
+ const normalizedPerPage = (0, _mastra_core_storage.normalizePerPage)(perPage, Number.MAX_SAFE_INTEGER);
1452
+ const offset = page * normalizedPerPage;
1453
+ runs = runs.slice(offset, offset + normalizedPerPage);
1454
+ }
1455
+ return {
1456
+ runs,
1457
+ total
1458
+ };
1459
+ } catch (error) {
1460
+ if (error instanceof _mastra_core_error.MastraError) throw error;
1461
+ throw new _mastra_core_error.MastraError({
1462
+ id: (0, _mastra_core_storage.createStorageErrorId)("REDIS", "LIST_WORKFLOW_RUNS", "FAILED"),
1463
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1464
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1465
+ details: {
1466
+ namespace: "workflows",
1467
+ workflowName: workflowName || "",
1468
+ resourceId: resourceId || ""
1469
+ }
1470
+ }, error);
1471
+ }
1472
+ }
1746
1473
  };
1747
-
1748
- // src/storage/utils.ts
1474
+ //#endregion
1475
+ //#region src/storage/utils.ts
1749
1476
  function isClientConfig(config) {
1750
- return "client" in config;
1477
+ return "client" in config;
1751
1478
  }
1752
1479
  function isConnectionStringConfig(config) {
1753
- return "connectionString" in config;
1480
+ return "connectionString" in config;
1754
1481
  }
1755
-
1756
- // src/storage/store.ts
1757
- var RedisStore = class extends storage.MastraStorage {
1758
- client;
1759
- shouldManageConnection;
1760
- stores;
1761
- constructor(config) {
1762
- super({ id: config.id, name: "Redis", disableInit: config.disableInit });
1763
- const { client, shouldManageConnection } = this.createClient(config);
1764
- this.client = client;
1765
- this.shouldManageConnection = shouldManageConnection;
1766
- this.stores = {
1767
- scores: new ScoresRedis({ client: this.client }),
1768
- workflows: new WorkflowsRedis({ client: this.client }),
1769
- memory: new StoreMemoryRedis({ client: this.client })
1770
- };
1771
- }
1772
- async init() {
1773
- if (this.shouldManageConnection && !this.client.isOpen) {
1774
- await this.client.connect();
1775
- }
1776
- await super.init();
1777
- }
1778
- getClient() {
1779
- return this.client;
1780
- }
1781
- async close() {
1782
- if (this.shouldManageConnection && this.client.isOpen) {
1783
- await this.client.quit();
1784
- }
1785
- }
1786
- createClient(config) {
1787
- if (isClientConfig(config)) {
1788
- return { client: config.client, shouldManageConnection: false };
1789
- }
1790
- if (isConnectionStringConfig(config)) {
1791
- if (!config.connectionString?.trim()) {
1792
- throw new Error("RedisStore: connectionString is required and cannot be empty.");
1793
- }
1794
- return {
1795
- client: redis.createClient({ url: config.connectionString }),
1796
- shouldManageConnection: true
1797
- };
1798
- }
1799
- if (!config.host?.trim()) {
1800
- throw new Error("RedisStore: host is required and cannot be empty.");
1801
- }
1802
- const url = this.createClientUrl({
1803
- ...config,
1804
- db: config.db ?? 0,
1805
- port: config.port ?? 6379
1806
- });
1807
- return {
1808
- client: redis.createClient({ url }),
1809
- shouldManageConnection: true
1810
- };
1811
- }
1812
- createClientUrl(config) {
1813
- const encodedPassword = config.password ? encodeURIComponent(config.password) : null;
1814
- if (config.password) {
1815
- return `redis://:${encodedPassword}@${config.host}:${config.port || 6379}/${config.db || 0}`;
1816
- }
1817
- return `redis://${config.host}:${config.port || 6379}/${config.db || 0}`;
1818
- }
1482
+ //#endregion
1483
+ //#region src/storage/store.ts
1484
+ /**
1485
+ * Redis storage adapter for Mastra.
1486
+ *
1487
+ * Provides storage functionality for direct Redis connections using the official redis package.
1488
+ *
1489
+ * Access domain-specific storage via `getStore()`:
1490
+ *
1491
+ * @example
1492
+ * ```typescript
1493
+ * // Using connection string
1494
+ * const storage = new RedisStore({
1495
+ * id: 'my-store',
1496
+ * connectionString: 'redis://localhost:6379',
1497
+ * });
1498
+ *
1499
+ * // Using host/port
1500
+ * const storage = new RedisStore({
1501
+ * id: 'my-store',
1502
+ * host: 'localhost',
1503
+ * port: 6379,
1504
+ * password: 'secret',
1505
+ * });
1506
+ *
1507
+ * // Access memory domain
1508
+ * const memory = await storage.getStore('memory');
1509
+ * await memory?.saveThread({ thread });
1510
+ *
1511
+ * // Access workflows domain
1512
+ * const workflows = await storage.getStore('workflows');
1513
+ * await workflows?.persistWorkflowSnapshot({ workflowName, runId, snapshot });
1514
+ * ```
1515
+ *
1516
+ * @example
1517
+ * ```typescript
1518
+ * // Using a pre-configured client for advanced features
1519
+ * import { createClient } from 'redis';
1520
+ *
1521
+ * const client = createClient({
1522
+ * url: 'redis://localhost:6379',
1523
+ * socket: {
1524
+ * reconnectStrategy: (retries) => Math.min(retries * 50, 2000),
1525
+ * },
1526
+ * });
1527
+ * await client.connect();
1528
+ *
1529
+ * const storage = new RedisStore({
1530
+ * id: 'my-store',
1531
+ * client,
1532
+ * });
1533
+ * ```
1534
+ */
1535
+ var RedisStore = class extends _mastra_core_storage.MastraStorage {
1536
+ client;
1537
+ shouldManageConnection;
1538
+ stores;
1539
+ constructor(config) {
1540
+ super({
1541
+ id: config.id,
1542
+ name: "Redis",
1543
+ disableInit: config.disableInit
1544
+ });
1545
+ const { client, shouldManageConnection } = this.createClient(config);
1546
+ this.client = client;
1547
+ this.shouldManageConnection = shouldManageConnection;
1548
+ this.stores = {
1549
+ scores: new ScoresRedis({ client: this.client }),
1550
+ workflows: new WorkflowsRedis({ client: this.client }),
1551
+ memory: new StoreMemoryRedis({ client: this.client })
1552
+ };
1553
+ }
1554
+ async init() {
1555
+ if (this.shouldManageConnection && !this.client.isOpen) await this.client.connect();
1556
+ await super.init();
1557
+ }
1558
+ getClient() {
1559
+ return this.client;
1560
+ }
1561
+ async close() {
1562
+ if (this.shouldManageConnection && this.client.isOpen) await this.client.quit();
1563
+ }
1564
+ createClient(config) {
1565
+ if (isClientConfig(config)) return {
1566
+ client: config.client,
1567
+ shouldManageConnection: false
1568
+ };
1569
+ if (isConnectionStringConfig(config)) {
1570
+ if (!config.connectionString?.trim()) throw new Error("RedisStore: connectionString is required and cannot be empty.");
1571
+ return {
1572
+ client: (0, redis.createClient)({ url: config.connectionString }),
1573
+ shouldManageConnection: true
1574
+ };
1575
+ }
1576
+ if (!config.host?.trim()) throw new Error("RedisStore: host is required and cannot be empty.");
1577
+ return {
1578
+ client: (0, redis.createClient)({ url: this.createClientUrl({
1579
+ ...config,
1580
+ db: config.db ?? 0,
1581
+ port: config.port ?? 6379
1582
+ }) }),
1583
+ shouldManageConnection: true
1584
+ };
1585
+ }
1586
+ createClientUrl(config) {
1587
+ const encodedPassword = config.password ? encodeURIComponent(config.password) : null;
1588
+ if (config.password) return `redis://:${encodedPassword}@${config.host}:${config.port || 6379}/${config.db || 0}`;
1589
+ return `redis://${config.host}:${config.port || 6379}/${config.db || 0}`;
1590
+ }
1819
1591
  };
1820
- var defaultSetWithExpiry = (client, key, value, seconds) => {
1821
- return client.set(key, value, "EX", seconds);
1592
+ //#endregion
1593
+ //#region src/cache.ts
1594
+ const defaultSetWithExpiry = (client, key, value, seconds) => {
1595
+ return client.set(key, value, "EX", seconds);
1822
1596
  };
1823
- var defaultScanKeys = (client, cursor, pattern, count) => {
1824
- return client.scan(cursor, "MATCH", pattern, "COUNT", count);
1597
+ const defaultScanKeys = (client, cursor, pattern, count) => {
1598
+ return client.scan(cursor, "MATCH", pattern, "COUNT", count);
1825
1599
  };
1826
- var defaultGetListLength = (client, key) => {
1827
- return client.llen(key);
1600
+ const defaultGetListLength = (client, key) => {
1601
+ return client.llen(key);
1828
1602
  };
1829
- var defaultPushToList = (client, key, value) => {
1830
- return client.rpush(key, value);
1603
+ const defaultPushToList = (client, key, value) => {
1604
+ return client.rpush(key, value);
1831
1605
  };
1832
- var defaultGetListRange = (client, key, start, stop) => {
1833
- return client.lrange(key, start, stop);
1606
+ const defaultGetListRange = (client, key, start, stop) => {
1607
+ return client.lrange(key, start, stop);
1834
1608
  };
1835
- var RedisServerCache = class extends cache.MastraServerCache {
1836
- client;
1837
- keyPrefix;
1838
- ttlSeconds;
1839
- setWithExpiry;
1840
- scanKeys;
1841
- getListLength;
1842
- pushToList;
1843
- getListRange;
1844
- constructor(config, options = {}) {
1845
- super({ name: "RedisServerCache" });
1846
- this.client = config.client;
1847
- this.keyPrefix = options.keyPrefix ?? "mastra:cache:";
1848
- this.ttlSeconds = options.ttlSeconds ?? 300;
1849
- this.setWithExpiry = options.setWithExpiry ?? defaultSetWithExpiry;
1850
- this.scanKeys = options.scanKeys ?? defaultScanKeys;
1851
- this.getListLength = options.getListLength ?? defaultGetListLength;
1852
- this.pushToList = options.pushToList ?? defaultPushToList;
1853
- this.getListRange = options.getListRange ?? defaultGetListRange;
1854
- }
1855
- getKey(key) {
1856
- return `${this.keyPrefix}${key}`;
1857
- }
1858
- serialize(value) {
1859
- return JSON.stringify(value);
1860
- }
1861
- deserialize(value) {
1862
- if (typeof value === "string") {
1863
- try {
1864
- return JSON.parse(value);
1865
- } catch {
1866
- return value;
1867
- }
1868
- }
1869
- return value;
1870
- }
1871
- async get(key) {
1872
- const fullKey = this.getKey(key);
1873
- const value = await this.client.get(fullKey);
1874
- if (value === null) {
1875
- return null;
1876
- }
1877
- return this.deserialize(value);
1878
- }
1879
- async set(key, value, ttlMs) {
1880
- const fullKey = this.getKey(key);
1881
- const serialized = this.serialize(value);
1882
- const overrideSeconds = ttlMs !== void 0 ? Math.max(1, Math.ceil(ttlMs / 1e3)) : void 0;
1883
- const effectiveSeconds = overrideSeconds ?? this.ttlSeconds;
1884
- if (effectiveSeconds > 0) {
1885
- await this.setWithExpiry(this.client, fullKey, serialized, effectiveSeconds);
1886
- } else {
1887
- await this.client.set(fullKey, serialized);
1888
- }
1889
- }
1890
- async listLength(key) {
1891
- const fullKey = this.getKey(key);
1892
- return this.getListLength(this.client, fullKey);
1893
- }
1894
- async listPush(key, value) {
1895
- const fullKey = this.getKey(key);
1896
- const serialized = this.serialize(value);
1897
- await this.pushToList(this.client, fullKey, serialized);
1898
- if (this.ttlSeconds > 0) {
1899
- await this.client.expire(fullKey, this.ttlSeconds);
1900
- }
1901
- }
1902
- async listFromTo(key, from, to = -1) {
1903
- const fullKey = this.getKey(key);
1904
- const values = await this.getListRange(this.client, fullKey, from, to);
1905
- return values.map((v) => this.deserialize(v));
1906
- }
1907
- async delete(key) {
1908
- const fullKey = this.getKey(key);
1909
- await this.client.del(fullKey);
1910
- }
1911
- async clear() {
1912
- const pattern = `${this.keyPrefix}*`;
1913
- let cursor = "0";
1914
- do {
1915
- const [nextCursor, keys] = await this.scanKeys(this.client, cursor, pattern, 100);
1916
- if (keys.length > 0) {
1917
- await this.client.del(...keys);
1918
- }
1919
- cursor = nextCursor;
1920
- } while (cursor !== "0" && cursor !== 0);
1921
- }
1922
- async increment(key) {
1923
- const fullKey = this.getKey(key);
1924
- return this.client.incr(fullKey);
1925
- }
1609
+ var RedisServerCache = class extends _mastra_core_cache.MastraServerCache {
1610
+ client;
1611
+ keyPrefix;
1612
+ ttlSeconds;
1613
+ setWithExpiry;
1614
+ scanKeys;
1615
+ getListLength;
1616
+ pushToList;
1617
+ getListRange;
1618
+ constructor(config, options = {}) {
1619
+ super({ name: "RedisServerCache" });
1620
+ this.client = config.client;
1621
+ this.keyPrefix = options.keyPrefix ?? "mastra:cache:";
1622
+ this.ttlSeconds = options.ttlSeconds ?? 300;
1623
+ this.setWithExpiry = options.setWithExpiry ?? defaultSetWithExpiry;
1624
+ this.scanKeys = options.scanKeys ?? defaultScanKeys;
1625
+ this.getListLength = options.getListLength ?? defaultGetListLength;
1626
+ this.pushToList = options.pushToList ?? defaultPushToList;
1627
+ this.getListRange = options.getListRange ?? defaultGetListRange;
1628
+ }
1629
+ getKey(key) {
1630
+ return `${this.keyPrefix}${key}`;
1631
+ }
1632
+ serialize(value) {
1633
+ return JSON.stringify(value);
1634
+ }
1635
+ deserialize(value) {
1636
+ if (typeof value === "string") try {
1637
+ return JSON.parse(value);
1638
+ } catch {
1639
+ return value;
1640
+ }
1641
+ return value;
1642
+ }
1643
+ async get(key) {
1644
+ const fullKey = this.getKey(key);
1645
+ const value = await this.client.get(fullKey);
1646
+ if (value === null) return null;
1647
+ return this.deserialize(value);
1648
+ }
1649
+ async set(key, value, ttlMs) {
1650
+ const fullKey = this.getKey(key);
1651
+ const serialized = this.serialize(value);
1652
+ const effectiveSeconds = (ttlMs !== void 0 ? Math.max(1, Math.ceil(ttlMs / 1e3)) : void 0) ?? this.ttlSeconds;
1653
+ if (effectiveSeconds > 0) await this.setWithExpiry(this.client, fullKey, serialized, effectiveSeconds);
1654
+ else await this.client.set(fullKey, serialized);
1655
+ }
1656
+ async listLength(key) {
1657
+ const fullKey = this.getKey(key);
1658
+ return this.getListLength(this.client, fullKey);
1659
+ }
1660
+ async listPush(key, value) {
1661
+ const fullKey = this.getKey(key);
1662
+ const serialized = this.serialize(value);
1663
+ await this.pushToList(this.client, fullKey, serialized);
1664
+ if (this.ttlSeconds > 0) await this.client.expire(fullKey, this.ttlSeconds);
1665
+ }
1666
+ async listFromTo(key, from, to = -1) {
1667
+ const fullKey = this.getKey(key);
1668
+ return (await this.getListRange(this.client, fullKey, from, to)).map((v) => this.deserialize(v));
1669
+ }
1670
+ async delete(key) {
1671
+ const fullKey = this.getKey(key);
1672
+ await this.client.del(fullKey);
1673
+ }
1674
+ async clear() {
1675
+ const pattern = `${this.keyPrefix}*`;
1676
+ let cursor = "0";
1677
+ do {
1678
+ const [nextCursor, keys] = await this.scanKeys(this.client, cursor, pattern, 100);
1679
+ if (keys.length > 0) await this.client.del(...keys);
1680
+ cursor = nextCursor;
1681
+ } while (cursor !== "0" && cursor !== 0);
1682
+ }
1683
+ async increment(key) {
1684
+ const fullKey = this.getKey(key);
1685
+ return this.client.incr(fullKey);
1686
+ }
1926
1687
  };
1927
- var upstashPreset = {
1928
- setWithExpiry: (client, key, value, seconds) => client.set(key, value, { ex: seconds }),
1929
- scanKeys: (client, cursor, pattern, count) => client.scan(cursor, { match: pattern, count })
1688
+ const upstashPreset = {
1689
+ setWithExpiry: (client, key, value, seconds) => client.set(key, value, { ex: seconds }),
1690
+ scanKeys: (client, cursor, pattern, count) => client.scan(cursor, {
1691
+ match: pattern,
1692
+ count
1693
+ })
1930
1694
  };
1931
- var nodeRedisPreset = {
1932
- setWithExpiry: (client, key, value, seconds) => client.set(key, value, { EX: seconds }),
1933
- scanKeys: (client, cursor, pattern, count) => client.scan(cursor, { MATCH: pattern, COUNT: count }),
1934
- getListLength: (client, key) => client.lLen(key),
1935
- pushToList: (client, key, value) => client.rPush(key, value),
1936
- getListRange: (client, key, start, stop) => client.lRange(key, start, stop)
1695
+ const nodeRedisPreset = {
1696
+ setWithExpiry: (client, key, value, seconds) => client.set(key, value, { EX: seconds }),
1697
+ scanKeys: (client, cursor, pattern, count) => client.scan(cursor, {
1698
+ MATCH: pattern,
1699
+ COUNT: count
1700
+ }),
1701
+ getListLength: (client, key) => client.lLen(key),
1702
+ pushToList: (client, key, value) => client.rPush(key, value),
1703
+ getListRange: (client, key, start, stop) => client.lRange(key, start, stop)
1937
1704
  };
1938
-
1705
+ //#endregion
1939
1706
  exports.RedisServerCache = RedisServerCache;
1940
1707
  exports.RedisStore = RedisStore;
1941
1708
  exports.ScoresRedis = ScoresRedis;
@@ -1943,5 +1710,5 @@ exports.StoreMemoryRedis = StoreMemoryRedis;
1943
1710
  exports.WorkflowsRedis = WorkflowsRedis;
1944
1711
  exports.nodeRedisPreset = nodeRedisPreset;
1945
1712
  exports.upstashPreset = upstashPreset;
1946
- //# sourceMappingURL=index.cjs.map
1713
+
1947
1714
  //# sourceMappingURL=index.cjs.map