@mastra/upstash 1.0.0-beta.0 → 1.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { MastraStorage, StoreOperations, ScoresStorage, TABLE_SCORERS, normalizePerPage, calculatePagination, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MemoryStorage, TABLE_THREADS, TABLE_RESOURCES, TABLE_MESSAGES, serializeDate } from '@mastra/core/storage';
1
+ import { MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ensureDate, createStorageErrorId, normalizePerPage, calculatePagination, filterByDateRange, ScoresStorage, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MastraStorage, createVectorErrorId, serializeDate, transformScoreRow as transformScoreRow$1 } from '@mastra/core/storage';
2
2
  import { Redis } from '@upstash/redis';
3
3
  import { MessageList } from '@mastra/core/agent';
4
4
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
@@ -9,13 +9,6 @@ import { Index } from '@upstash/vector';
9
9
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
10
10
 
11
11
  // src/storage/index.ts
12
- function ensureDate(value) {
13
- if (!value) return null;
14
- if (value instanceof Date) return value;
15
- if (typeof value === "string") return new Date(value);
16
- if (typeof value === "number") return new Date(value);
17
- return null;
18
- }
19
12
  function getKey(tableName, keys) {
20
13
  const keyParts = Object.entries(keys).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`);
21
14
  return `${tableName}:${keyParts.join(":")}`;
@@ -32,7 +25,7 @@ function processRecord(tableName, record) {
32
25
  ...record.resourceId ? { resourceId: record.resourceId } : {}
33
26
  });
34
27
  } else if (tableName === TABLE_SCORERS) {
35
- key = getKey(tableName, { runId: record.runId });
28
+ key = getKey(tableName, { id: record.id });
36
29
  } else {
37
30
  key = getKey(tableName, { id: record.id });
38
31
  }
@@ -44,6 +37,107 @@ function processRecord(tableName, record) {
44
37
  return { key, processedRecord };
45
38
  }
46
39
 
40
+ // src/storage/db/index.ts
41
+ function resolveUpstashConfig(config) {
42
+ if ("client" in config) {
43
+ return config.client;
44
+ }
45
+ return new Redis({
46
+ url: config.url,
47
+ token: config.token
48
+ });
49
+ }
50
+ var UpstashDB = class {
51
+ client;
52
+ constructor({ client }) {
53
+ this.client = client;
54
+ }
55
+ async insert({ tableName, record }) {
56
+ const { key, processedRecord } = processRecord(tableName, record);
57
+ try {
58
+ await this.client.set(key, processedRecord);
59
+ } catch (error) {
60
+ throw new MastraError(
61
+ {
62
+ id: createStorageErrorId("UPSTASH", "INSERT", "FAILED"),
63
+ domain: ErrorDomain.STORAGE,
64
+ category: ErrorCategory.THIRD_PARTY,
65
+ details: {
66
+ tableName
67
+ }
68
+ },
69
+ error
70
+ );
71
+ }
72
+ }
73
+ async get({ tableName, keys }) {
74
+ const key = getKey(tableName, keys);
75
+ try {
76
+ const data = await this.client.get(key);
77
+ return data || null;
78
+ } catch (error) {
79
+ throw new MastraError(
80
+ {
81
+ id: createStorageErrorId("UPSTASH", "LOAD", "FAILED"),
82
+ domain: ErrorDomain.STORAGE,
83
+ category: ErrorCategory.THIRD_PARTY,
84
+ details: {
85
+ tableName
86
+ }
87
+ },
88
+ error
89
+ );
90
+ }
91
+ }
92
+ async scanAndDelete(pattern, batchSize = 1e4) {
93
+ let cursor = "0";
94
+ let totalDeleted = 0;
95
+ do {
96
+ const [nextCursor, keys] = await this.client.scan(cursor, {
97
+ match: pattern,
98
+ count: batchSize
99
+ });
100
+ if (keys.length > 0) {
101
+ await this.client.del(...keys);
102
+ totalDeleted += keys.length;
103
+ }
104
+ cursor = nextCursor;
105
+ } while (cursor !== "0");
106
+ return totalDeleted;
107
+ }
108
+ async scanKeys(pattern, batchSize = 1e4) {
109
+ let cursor = "0";
110
+ let keys = [];
111
+ do {
112
+ const [nextCursor, batch] = await this.client.scan(cursor, {
113
+ match: pattern,
114
+ count: batchSize
115
+ });
116
+ keys.push(...batch);
117
+ cursor = nextCursor;
118
+ } while (cursor !== "0");
119
+ return keys;
120
+ }
121
+ async deleteData({ tableName }) {
122
+ const pattern = `${tableName}:*`;
123
+ try {
124
+ await this.scanAndDelete(pattern);
125
+ } catch (error) {
126
+ throw new MastraError(
127
+ {
128
+ id: createStorageErrorId("UPSTASH", "CLEAR_TABLE", "FAILED"),
129
+ domain: ErrorDomain.STORAGE,
130
+ category: ErrorCategory.THIRD_PARTY,
131
+ details: {
132
+ tableName
133
+ }
134
+ },
135
+ error
136
+ );
137
+ }
138
+ }
139
+ };
140
+
47
141
  // src/storage/domains/memory/index.ts
48
142
  function getThreadMessagesKey(threadId) {
49
143
  return `thread:${threadId}:messages`;
@@ -52,17 +146,26 @@ function getMessageKey(threadId, messageId) {
52
146
  const key = getKey(TABLE_MESSAGES, { threadId, id: messageId });
53
147
  return key;
54
148
  }
149
+ function getMessageIndexKey(messageId) {
150
+ return `msg-idx:${messageId}`;
151
+ }
55
152
  var StoreMemoryUpstash = class extends MemoryStorage {
56
153
  client;
57
- operations;
58
- constructor({ client, operations }) {
154
+ #db;
155
+ constructor(config) {
59
156
  super();
157
+ const client = resolveUpstashConfig(config);
60
158
  this.client = client;
61
- this.operations = operations;
159
+ this.#db = new UpstashDB({ client });
160
+ }
161
+ async dangerouslyClearAll() {
162
+ await this.#db.deleteData({ tableName: TABLE_THREADS });
163
+ await this.#db.deleteData({ tableName: TABLE_MESSAGES });
164
+ await this.#db.deleteData({ tableName: TABLE_RESOURCES });
62
165
  }
63
166
  async getThreadById({ threadId }) {
64
167
  try {
65
- const thread = await this.operations.load({
168
+ const thread = await this.#db.get({
66
169
  tableName: TABLE_THREADS,
67
170
  keys: { id: threadId }
68
171
  });
@@ -76,7 +179,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
76
179
  } catch (error) {
77
180
  throw new MastraError(
78
181
  {
79
- id: "STORAGE_UPSTASH_STORAGE_GET_THREAD_BY_ID_FAILED",
182
+ id: createStorageErrorId("UPSTASH", "GET_THREAD_BY_ID", "FAILED"),
80
183
  domain: ErrorDomain.STORAGE,
81
184
  category: ErrorCategory.THIRD_PARTY,
82
185
  details: {
@@ -94,7 +197,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
94
197
  if (page < 0) {
95
198
  throw new MastraError(
96
199
  {
97
- id: "STORAGE_UPSTASH_LIST_THREADS_BY_RESOURCE_ID_INVALID_PAGE",
200
+ id: createStorageErrorId("UPSTASH", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
98
201
  domain: ErrorDomain.STORAGE,
99
202
  category: ErrorCategory.USER,
100
203
  details: { page }
@@ -106,7 +209,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
106
209
  try {
107
210
  let allThreads = [];
108
211
  const pattern = `${TABLE_THREADS}:*`;
109
- const keys = await this.operations.scanKeys(pattern);
212
+ const keys = await this.#db.scanKeys(pattern);
110
213
  const pipeline = this.client.pipeline();
111
214
  keys.forEach((key) => pipeline.get(key));
112
215
  const results = await pipeline.exec();
@@ -136,7 +239,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
136
239
  } catch (error) {
137
240
  const mastraError = new MastraError(
138
241
  {
139
- id: "STORAGE_UPSTASH_STORAGE_LIST_THREADS_BY_RESOURCE_ID_FAILED",
242
+ id: createStorageErrorId("UPSTASH", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
140
243
  domain: ErrorDomain.STORAGE,
141
244
  category: ErrorCategory.THIRD_PARTY,
142
245
  details: {
@@ -160,7 +263,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
160
263
  }
161
264
  async saveThread({ thread }) {
162
265
  try {
163
- await this.operations.insert({
266
+ await this.#db.insert({
164
267
  tableName: TABLE_THREADS,
165
268
  record: thread
166
269
  });
@@ -168,7 +271,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
168
271
  } catch (error) {
169
272
  const mastraError = new MastraError(
170
273
  {
171
- id: "STORAGE_UPSTASH_STORAGE_SAVE_THREAD_FAILED",
274
+ id: createStorageErrorId("UPSTASH", "SAVE_THREAD", "FAILED"),
172
275
  domain: ErrorDomain.STORAGE,
173
276
  category: ErrorCategory.THIRD_PARTY,
174
277
  details: {
@@ -190,7 +293,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
190
293
  const thread = await this.getThreadById({ threadId: id });
191
294
  if (!thread) {
192
295
  throw new MastraError({
193
- id: "STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED",
296
+ id: createStorageErrorId("UPSTASH", "UPDATE_THREAD", "FAILED"),
194
297
  domain: ErrorDomain.STORAGE,
195
298
  category: ErrorCategory.USER,
196
299
  text: `Thread ${id} not found`,
@@ -213,7 +316,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
213
316
  } catch (error) {
214
317
  throw new MastraError(
215
318
  {
216
- id: "STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED",
319
+ id: createStorageErrorId("UPSTASH", "UPDATE_THREAD", "FAILED"),
217
320
  domain: ErrorDomain.STORAGE,
218
321
  category: ErrorCategory.THIRD_PARTY,
219
322
  details: {
@@ -238,11 +341,11 @@ var StoreMemoryUpstash = class extends MemoryStorage {
238
341
  pipeline.del(messageKey);
239
342
  }
240
343
  await pipeline.exec();
241
- await this.operations.scanAndDelete(getMessageKey(threadId, "*"));
344
+ await this.#db.scanAndDelete(getMessageKey(threadId, "*"));
242
345
  } catch (error) {
243
346
  throw new MastraError(
244
347
  {
245
- id: "STORAGE_UPSTASH_STORAGE_DELETE_THREAD_FAILED",
348
+ id: createStorageErrorId("UPSTASH", "DELETE_THREAD", "FAILED"),
246
349
  domain: ErrorDomain.STORAGE,
247
350
  category: ErrorCategory.THIRD_PARTY,
248
351
  details: {
@@ -268,7 +371,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
268
371
  } catch (error) {
269
372
  throw new MastraError(
270
373
  {
271
- id: "STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_INVALID_ARGS",
374
+ id: createStorageErrorId("UPSTASH", "SAVE_MESSAGES", "INVALID_ARGS"),
272
375
  domain: ErrorDomain.STORAGE,
273
376
  category: ErrorCategory.USER
274
377
  },
@@ -303,7 +406,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
303
406
  const createdAtScore = new Date(message.createdAt).getTime();
304
407
  const score = message._index !== void 0 ? message._index : createdAtScore;
305
408
  const existingKeyPattern = getMessageKey("*", message.id);
306
- const keys = await this.operations.scanKeys(existingKeyPattern);
409
+ const keys = await this.#db.scanKeys(existingKeyPattern);
307
410
  if (keys.length > 0) {
308
411
  const pipeline2 = this.client.pipeline();
309
412
  keys.forEach((key2) => pipeline2.get(key2));
@@ -318,6 +421,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
318
421
  }
319
422
  }
320
423
  pipeline.set(key, message);
424
+ pipeline.set(getMessageIndexKey(message.id), message.threadId);
321
425
  pipeline.zadd(getThreadMessagesKey(message.threadId), {
322
426
  score,
323
427
  member: message.id
@@ -337,7 +441,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
337
441
  } catch (error) {
338
442
  throw new MastraError(
339
443
  {
340
- id: "STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_FAILED",
444
+ id: createStorageErrorId("UPSTASH", "SAVE_MESSAGES", "FAILED"),
341
445
  domain: ErrorDomain.STORAGE,
342
446
  category: ErrorCategory.THIRD_PARTY,
343
447
  details: {
@@ -348,43 +452,60 @@ var StoreMemoryUpstash = class extends MemoryStorage {
348
452
  );
349
453
  }
350
454
  }
351
- async _getIncludedMessages(threadId, include) {
352
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
455
+ /**
456
+ * Lookup threadId for a message - tries index first (O(1)), falls back to scan (backwards compatible)
457
+ */
458
+ async _getThreadIdForMessage(messageId) {
459
+ const indexedThreadId = await this.client.get(getMessageIndexKey(messageId));
460
+ if (indexedThreadId) {
461
+ return indexedThreadId;
462
+ }
463
+ const existingKeyPattern = getMessageKey("*", messageId);
464
+ const keys = await this.#db.scanKeys(existingKeyPattern);
465
+ if (keys.length === 0) return null;
466
+ const messageData = await this.client.get(keys[0]);
467
+ if (!messageData) return null;
468
+ if (messageData.threadId) {
469
+ await this.client.set(getMessageIndexKey(messageId), messageData.threadId);
470
+ }
471
+ return messageData.threadId || null;
472
+ }
473
+ async _getIncludedMessages(include) {
474
+ if (!include?.length) return [];
353
475
  const messageIds = /* @__PURE__ */ new Set();
354
476
  const messageIdToThreadIds = {};
355
- if (include?.length) {
356
- for (const item of include) {
357
- messageIds.add(item.id);
358
- const itemThreadId = item.threadId || threadId;
359
- messageIdToThreadIds[item.id] = itemThreadId;
360
- const itemThreadMessagesKey = getThreadMessagesKey(itemThreadId);
361
- const rank = await this.client.zrank(itemThreadMessagesKey, item.id);
362
- if (rank === null) continue;
363
- if (item.withPreviousMessages) {
364
- const start = Math.max(0, rank - item.withPreviousMessages);
365
- const prevIds = rank === 0 ? [] : await this.client.zrange(itemThreadMessagesKey, start, rank - 1);
366
- prevIds.forEach((id) => {
367
- messageIds.add(id);
368
- messageIdToThreadIds[id] = itemThreadId;
369
- });
370
- }
371
- if (item.withNextMessages) {
372
- const nextIds = await this.client.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
373
- nextIds.forEach((id) => {
374
- messageIds.add(id);
375
- messageIdToThreadIds[id] = itemThreadId;
376
- });
377
- }
477
+ for (const item of include) {
478
+ const itemThreadId = await this._getThreadIdForMessage(item.id);
479
+ if (!itemThreadId) continue;
480
+ messageIds.add(item.id);
481
+ messageIdToThreadIds[item.id] = itemThreadId;
482
+ const itemThreadMessagesKey = getThreadMessagesKey(itemThreadId);
483
+ const rank = await this.client.zrank(itemThreadMessagesKey, item.id);
484
+ if (rank === null) continue;
485
+ if (item.withPreviousMessages) {
486
+ const start = Math.max(0, rank - item.withPreviousMessages);
487
+ const prevIds = rank === 0 ? [] : await this.client.zrange(itemThreadMessagesKey, start, rank - 1);
488
+ prevIds.forEach((id) => {
489
+ messageIds.add(id);
490
+ messageIdToThreadIds[id] = itemThreadId;
491
+ });
492
+ }
493
+ if (item.withNextMessages) {
494
+ const nextIds = await this.client.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
495
+ nextIds.forEach((id) => {
496
+ messageIds.add(id);
497
+ messageIdToThreadIds[id] = itemThreadId;
498
+ });
378
499
  }
379
- const pipeline = this.client.pipeline();
380
- Array.from(messageIds).forEach((id) => {
381
- const tId = messageIdToThreadIds[id] || threadId;
382
- pipeline.get(getMessageKey(tId, id));
383
- });
384
- const results = await pipeline.exec();
385
- return results.filter((result) => result !== null);
386
500
  }
387
- return [];
501
+ if (messageIds.size === 0) return [];
502
+ const pipeline = this.client.pipeline();
503
+ Array.from(messageIds).forEach((id) => {
504
+ const tId = messageIdToThreadIds[id];
505
+ pipeline.get(getMessageKey(tId, id));
506
+ });
507
+ const results = await pipeline.exec();
508
+ return results.filter((result) => result !== null);
388
509
  }
389
510
  parseStoredMessage(storedMessage) {
390
511
  const defaultMessageContent = { format: 2, parts: [{ type: "text", text: "" }] };
@@ -398,23 +519,55 @@ var StoreMemoryUpstash = class extends MemoryStorage {
398
519
  async listMessagesById({ messageIds }) {
399
520
  if (messageIds.length === 0) return { messages: [] };
400
521
  try {
401
- const threadKeys = await this.client.keys("thread:*");
402
- const result = await Promise.all(
403
- threadKeys.map((threadKey) => {
404
- const threadId = threadKey.split(":")[1];
405
- if (!threadId) throw new Error(`Failed to parse thread ID from thread key "${threadKey}"`);
406
- return this.client.mget(
407
- messageIds.map((id) => getMessageKey(threadId, id))
408
- );
409
- })
410
- );
411
- const rawMessages = result.flat(1).filter((msg) => !!msg);
522
+ const rawMessages = [];
523
+ const indexPipeline = this.client.pipeline();
524
+ messageIds.forEach((id) => indexPipeline.get(getMessageIndexKey(id)));
525
+ const indexResults = await indexPipeline.exec();
526
+ const indexedIds = [];
527
+ const unindexedIds = [];
528
+ messageIds.forEach((id, i) => {
529
+ const threadId = indexResults[i];
530
+ if (threadId) {
531
+ indexedIds.push({ messageId: id, threadId });
532
+ } else {
533
+ unindexedIds.push(id);
534
+ }
535
+ });
536
+ if (indexedIds.length > 0) {
537
+ const messagePipeline = this.client.pipeline();
538
+ indexedIds.forEach(({ messageId, threadId }) => messagePipeline.get(getMessageKey(threadId, messageId)));
539
+ const messageResults = await messagePipeline.exec();
540
+ rawMessages.push(...messageResults.filter((msg) => msg !== null));
541
+ }
542
+ if (unindexedIds.length > 0) {
543
+ const threadKeys = await this.client.keys("thread:*");
544
+ const result = await Promise.all(
545
+ threadKeys.map((threadKey) => {
546
+ const threadId = threadKey.split(":")[1];
547
+ if (!threadId) throw new Error(`Failed to parse thread ID from thread key "${threadKey}"`);
548
+ return this.client.mget(
549
+ unindexedIds.map((id) => getMessageKey(threadId, id))
550
+ );
551
+ })
552
+ );
553
+ const foundMessages = result.flat(1).filter((msg) => !!msg);
554
+ rawMessages.push(...foundMessages);
555
+ if (foundMessages.length > 0) {
556
+ const backfillPipeline = this.client.pipeline();
557
+ foundMessages.forEach((msg) => {
558
+ if (msg.threadId) {
559
+ backfillPipeline.set(getMessageIndexKey(msg.id), msg.threadId);
560
+ }
561
+ });
562
+ await backfillPipeline.exec();
563
+ }
564
+ }
412
565
  const list = new MessageList().add(rawMessages.map(this.parseStoredMessage), "memory");
413
566
  return { messages: list.get.all.db() };
414
567
  } catch (error) {
415
568
  throw new MastraError(
416
569
  {
417
- id: "STORAGE_UPSTASH_STORAGE_LIST_MESSAGES_BY_ID_FAILED",
570
+ id: createStorageErrorId("UPSTASH", "LIST_MESSAGES_BY_ID", "FAILED"),
418
571
  domain: ErrorDomain.STORAGE,
419
572
  category: ErrorCategory.THIRD_PARTY,
420
573
  details: {
@@ -427,25 +580,25 @@ var StoreMemoryUpstash = class extends MemoryStorage {
427
580
  }
428
581
  async listMessages(args) {
429
582
  const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
430
- if (!threadId.trim()) {
583
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
584
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
431
585
  throw new MastraError(
432
586
  {
433
- id: "STORAGE_UPSTASH_LIST_MESSAGES_INVALID_THREAD_ID",
587
+ id: createStorageErrorId("UPSTASH", "LIST_MESSAGES", "INVALID_THREAD_ID"),
434
588
  domain: ErrorDomain.STORAGE,
435
589
  category: ErrorCategory.THIRD_PARTY,
436
- details: { threadId }
590
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
437
591
  },
438
- new Error("threadId must be a non-empty string")
592
+ new Error("threadId must be a non-empty string or array of non-empty strings")
439
593
  );
440
594
  }
441
- const threadMessagesKey = getThreadMessagesKey(threadId);
442
595
  const perPage = normalizePerPage(perPageInput, 40);
443
596
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
444
597
  try {
445
598
  if (page < 0) {
446
599
  throw new MastraError(
447
600
  {
448
- id: "STORAGE_UPSTASH_LIST_MESSAGES_INVALID_PAGE",
601
+ id: createStorageErrorId("UPSTASH", "LIST_MESSAGES", "INVALID_PAGE"),
449
602
  domain: ErrorDomain.STORAGE,
450
603
  category: ErrorCategory.USER,
451
604
  details: { page }
@@ -455,11 +608,18 @@ var StoreMemoryUpstash = class extends MemoryStorage {
455
608
  }
456
609
  let includedMessages = [];
457
610
  if (include && include.length > 0) {
458
- const included = await this._getIncludedMessages(threadId, include);
611
+ const included = await this._getIncludedMessages(include);
459
612
  includedMessages = included.map(this.parseStoredMessage);
460
613
  }
461
- const allMessageIds = await this.client.zrange(threadMessagesKey, 0, -1);
462
- if (allMessageIds.length === 0) {
614
+ const allMessageIdsWithThreads = [];
615
+ for (const tid of threadIds) {
616
+ const threadMessagesKey = getThreadMessagesKey(tid);
617
+ const messageIds2 = await this.client.zrange(threadMessagesKey, 0, -1);
618
+ for (const mid of messageIds2) {
619
+ allMessageIdsWithThreads.push({ threadId: tid, messageId: mid });
620
+ }
621
+ }
622
+ if (allMessageIdsWithThreads.length === 0) {
463
623
  return {
464
624
  messages: [],
465
625
  total: 0,
@@ -469,21 +629,17 @@ var StoreMemoryUpstash = class extends MemoryStorage {
469
629
  };
470
630
  }
471
631
  const pipeline = this.client.pipeline();
472
- allMessageIds.forEach((id) => pipeline.get(getMessageKey(threadId, id)));
632
+ allMessageIdsWithThreads.forEach(({ threadId: tid, messageId }) => pipeline.get(getMessageKey(tid, messageId)));
473
633
  const results = await pipeline.exec();
474
634
  let messagesData = results.filter((msg) => msg !== null).map(this.parseStoredMessage);
475
635
  if (resourceId) {
476
636
  messagesData = messagesData.filter((msg) => msg.resourceId === resourceId);
477
637
  }
478
- const dateRange = filter?.dateRange;
479
- if (dateRange?.start) {
480
- const fromDate = dateRange.start;
481
- messagesData = messagesData.filter((msg) => new Date(msg.createdAt).getTime() >= fromDate.getTime());
482
- }
483
- if (dateRange?.end) {
484
- const toDate = dateRange.end;
485
- messagesData = messagesData.filter((msg) => new Date(msg.createdAt).getTime() <= toDate.getTime());
486
- }
638
+ messagesData = filterByDateRange(
639
+ messagesData,
640
+ (msg) => new Date(msg.createdAt),
641
+ filter?.dateRange
642
+ );
487
643
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
488
644
  const getFieldValue = (msg) => {
489
645
  if (field === "createdAt") {
@@ -498,13 +654,11 @@ var StoreMemoryUpstash = class extends MemoryStorage {
498
654
  }
499
655
  return 0;
500
656
  };
501
- if (orderBy) {
502
- messagesData.sort((a, b) => {
503
- const aValue = getFieldValue(a);
504
- const bValue = getFieldValue(b);
505
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
506
- });
507
- }
657
+ messagesData.sort((a, b) => {
658
+ const aValue = getFieldValue(a);
659
+ const bValue = getFieldValue(b);
660
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
661
+ });
508
662
  const total = messagesData.length;
509
663
  const start = offset;
510
664
  const end = perPageInput === false ? total : start + perPage;
@@ -525,23 +679,11 @@ var StoreMemoryUpstash = class extends MemoryStorage {
525
679
  }
526
680
  const list = new MessageList().add(allMessages, "memory");
527
681
  let finalMessages = list.get.all.db();
528
- if (orderBy) {
529
- finalMessages = finalMessages.sort((a, b) => {
530
- const aValue = getFieldValue(a);
531
- const bValue = getFieldValue(b);
532
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
533
- });
534
- } else {
535
- const messageIdToPosition = /* @__PURE__ */ new Map();
536
- allMessageIds.forEach((id, index) => {
537
- messageIdToPosition.set(id, index);
538
- });
539
- finalMessages = finalMessages.sort((a, b) => {
540
- const aPos = messageIdToPosition.get(a.id) ?? Number.MAX_SAFE_INTEGER;
541
- const bPos = messageIdToPosition.get(b.id) ?? Number.MAX_SAFE_INTEGER;
542
- return aPos - bPos;
543
- });
544
- }
682
+ finalMessages = finalMessages.sort((a, b) => {
683
+ const aValue = getFieldValue(a);
684
+ const bValue = getFieldValue(b);
685
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
686
+ });
545
687
  const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
546
688
  const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
547
689
  const hasMore = perPageInput !== false && !allThreadMessagesReturned && end < total;
@@ -555,11 +697,11 @@ var StoreMemoryUpstash = class extends MemoryStorage {
555
697
  } catch (error) {
556
698
  const mastraError = new MastraError(
557
699
  {
558
- id: "STORAGE_UPSTASH_STORAGE_LIST_MESSAGES_FAILED",
700
+ id: createStorageErrorId("UPSTASH", "LIST_MESSAGES", "FAILED"),
559
701
  domain: ErrorDomain.STORAGE,
560
702
  category: ErrorCategory.THIRD_PARTY,
561
703
  details: {
562
- threadId,
704
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
563
705
  resourceId: resourceId ?? ""
564
706
  }
565
707
  },
@@ -656,7 +798,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
656
798
  const messageIdToKey = {};
657
799
  for (const messageId of messageIds) {
658
800
  const pattern = getMessageKey("*", messageId);
659
- const keys = await this.operations.scanKeys(pattern);
801
+ const keys = await this.#db.scanKeys(pattern);
660
802
  for (const key of keys) {
661
803
  const message = await this.client.get(key);
662
804
  if (message && message.id === messageId) {
@@ -746,7 +888,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
746
888
  } catch (error) {
747
889
  throw new MastraError(
748
890
  {
749
- id: "STORAGE_UPSTASH_STORAGE_UPDATE_MESSAGES_FAILED",
891
+ id: createStorageErrorId("UPSTASH", "UPDATE_MESSAGES", "FAILED"),
750
892
  domain: ErrorDomain.STORAGE,
751
893
  category: ErrorCategory.THIRD_PARTY,
752
894
  details: {
@@ -764,13 +906,33 @@ var StoreMemoryUpstash = class extends MemoryStorage {
764
906
  try {
765
907
  const threadIds = /* @__PURE__ */ new Set();
766
908
  const messageKeys = [];
767
- for (const messageId of messageIds) {
909
+ const foundMessageIds = [];
910
+ const indexPipeline = this.client.pipeline();
911
+ messageIds.forEach((id) => indexPipeline.get(getMessageIndexKey(id)));
912
+ const indexResults = await indexPipeline.exec();
913
+ const indexedMessages = [];
914
+ const unindexedMessageIds = [];
915
+ messageIds.forEach((id, i) => {
916
+ const threadId = indexResults[i];
917
+ if (threadId) {
918
+ indexedMessages.push({ messageId: id, threadId });
919
+ } else {
920
+ unindexedMessageIds.push(id);
921
+ }
922
+ });
923
+ for (const { messageId, threadId } of indexedMessages) {
924
+ messageKeys.push(getMessageKey(threadId, messageId));
925
+ foundMessageIds.push(messageId);
926
+ threadIds.add(threadId);
927
+ }
928
+ for (const messageId of unindexedMessageIds) {
768
929
  const pattern = getMessageKey("*", messageId);
769
- const keys = await this.operations.scanKeys(pattern);
930
+ const keys = await this.#db.scanKeys(pattern);
770
931
  for (const key of keys) {
771
932
  const message = await this.client.get(key);
772
933
  if (message && message.id === messageId) {
773
934
  messageKeys.push(key);
935
+ foundMessageIds.push(messageId);
774
936
  if (message.threadId) {
775
937
  threadIds.add(message.threadId);
776
938
  }
@@ -785,6 +947,9 @@ var StoreMemoryUpstash = class extends MemoryStorage {
785
947
  for (const key of messageKeys) {
786
948
  pipeline.del(key);
787
949
  }
950
+ for (const messageId of foundMessageIds) {
951
+ pipeline.del(getMessageIndexKey(messageId));
952
+ }
788
953
  if (threadIds.size > 0) {
789
954
  for (const threadId of threadIds) {
790
955
  const threadKey = getKey(TABLE_THREADS, { id: threadId });
@@ -802,7 +967,7 @@ var StoreMemoryUpstash = class extends MemoryStorage {
802
967
  } catch (error) {
803
968
  throw new MastraError(
804
969
  {
805
- id: "STORAGE_UPSTASH_DELETE_MESSAGES_FAILED",
970
+ id: createStorageErrorId("UPSTASH", "DELETE_MESSAGES", "FAILED"),
806
971
  domain: ErrorDomain.STORAGE,
807
972
  category: ErrorCategory.THIRD_PARTY,
808
973
  details: { messageIds: messageIds.join(", ") }
@@ -822,182 +987,137 @@ var StoreMemoryUpstash = class extends MemoryStorage {
822
987
  }
823
988
  });
824
989
  }
825
- };
826
- var StoreOperationsUpstash = class extends StoreOperations {
827
- client;
828
- constructor({ client }) {
829
- super();
830
- this.client = client;
831
- }
832
- async createTable({
833
- tableName: _tableName,
834
- schema: _schema
835
- }) {
836
- }
837
- async alterTable({
838
- tableName: _tableName,
839
- schema: _schema,
840
- ifNotExists: _ifNotExists
841
- }) {
842
- }
843
- async clearTable({ tableName }) {
844
- const pattern = `${tableName}:*`;
845
- try {
846
- await this.scanAndDelete(pattern);
847
- } catch (error) {
848
- throw new MastraError(
849
- {
850
- id: "STORAGE_UPSTASH_STORAGE_CLEAR_TABLE_FAILED",
851
- domain: ErrorDomain.STORAGE,
852
- category: ErrorCategory.THIRD_PARTY,
853
- details: {
854
- tableName
855
- }
856
- },
857
- error
858
- );
990
+ async cloneThread(args) {
991
+ const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args;
992
+ const sourceThread = await this.getThreadById({ threadId: sourceThreadId });
993
+ if (!sourceThread) {
994
+ throw new MastraError({
995
+ id: createStorageErrorId("UPSTASH", "CLONE_THREAD", "SOURCE_NOT_FOUND"),
996
+ domain: ErrorDomain.STORAGE,
997
+ category: ErrorCategory.USER,
998
+ text: `Source thread with id ${sourceThreadId} not found`,
999
+ details: { sourceThreadId }
1000
+ });
859
1001
  }
860
- }
861
- async dropTable({ tableName }) {
862
- return this.clearTable({ tableName });
863
- }
864
- async insert({ tableName, record }) {
865
- const { key, processedRecord } = processRecord(tableName, record);
866
- try {
867
- await this.client.set(key, processedRecord);
868
- } catch (error) {
869
- throw new MastraError(
870
- {
871
- id: "STORAGE_UPSTASH_STORAGE_INSERT_FAILED",
872
- domain: ErrorDomain.STORAGE,
873
- category: ErrorCategory.THIRD_PARTY,
874
- details: {
875
- tableName
876
- }
877
- },
878
- error
879
- );
1002
+ const newThreadId = providedThreadId || crypto.randomUUID();
1003
+ const existingThread = await this.getThreadById({ threadId: newThreadId });
1004
+ if (existingThread) {
1005
+ throw new MastraError({
1006
+ id: createStorageErrorId("UPSTASH", "CLONE_THREAD", "THREAD_EXISTS"),
1007
+ domain: ErrorDomain.STORAGE,
1008
+ category: ErrorCategory.USER,
1009
+ text: `Thread with id ${newThreadId} already exists`,
1010
+ details: { newThreadId }
1011
+ });
880
1012
  }
881
- }
882
- async batchInsert(input) {
883
- const { tableName, records } = input;
884
- if (!records.length) return;
885
- const batchSize = 1e3;
886
1013
  try {
887
- for (let i = 0; i < records.length; i += batchSize) {
888
- const batch = records.slice(i, i + batchSize);
889
- const pipeline = this.client.pipeline();
890
- for (const record of batch) {
891
- const { key, processedRecord } = processRecord(tableName, record);
892
- pipeline.set(key, processedRecord);
893
- }
894
- await pipeline.exec();
1014
+ const threadMessagesKey = getThreadMessagesKey(sourceThreadId);
1015
+ const messageIds = await this.client.zrange(threadMessagesKey, 0, -1);
1016
+ const pipeline = this.client.pipeline();
1017
+ for (const mid of messageIds) {
1018
+ pipeline.get(getMessageKey(sourceThreadId, mid));
895
1019
  }
896
- } catch (error) {
897
- throw new MastraError(
898
- {
899
- id: "STORAGE_UPSTASH_STORAGE_BATCH_INSERT_FAILED",
900
- domain: ErrorDomain.STORAGE,
901
- category: ErrorCategory.THIRD_PARTY,
902
- details: {
903
- tableName
904
- }
1020
+ const results = await pipeline.exec();
1021
+ let sourceMessages = results.filter((msg) => msg !== null).map((msg) => ({
1022
+ ...msg,
1023
+ createdAt: new Date(msg.createdAt)
1024
+ }));
1025
+ if (options?.messageFilter?.startDate || options?.messageFilter?.endDate) {
1026
+ sourceMessages = filterByDateRange(sourceMessages, (msg) => new Date(msg.createdAt), {
1027
+ start: options.messageFilter?.startDate,
1028
+ end: options.messageFilter?.endDate
1029
+ });
1030
+ }
1031
+ if (options?.messageFilter?.messageIds && options.messageFilter.messageIds.length > 0) {
1032
+ const messageIdSet = new Set(options.messageFilter.messageIds);
1033
+ sourceMessages = sourceMessages.filter((msg) => messageIdSet.has(msg.id));
1034
+ }
1035
+ sourceMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1036
+ if (options?.messageLimit && options.messageLimit > 0 && sourceMessages.length > options.messageLimit) {
1037
+ sourceMessages = sourceMessages.slice(-options.messageLimit);
1038
+ }
1039
+ const now = /* @__PURE__ */ new Date();
1040
+ const lastMessageId = sourceMessages.length > 0 ? sourceMessages[sourceMessages.length - 1].id : void 0;
1041
+ const cloneMetadata = {
1042
+ sourceThreadId,
1043
+ clonedAt: now,
1044
+ ...lastMessageId && { lastMessageId }
1045
+ };
1046
+ const newThread = {
1047
+ id: newThreadId,
1048
+ resourceId: resourceId || sourceThread.resourceId,
1049
+ title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : void 0),
1050
+ metadata: {
1051
+ ...metadata,
1052
+ clone: cloneMetadata
905
1053
  },
906
- error
907
- );
908
- }
909
- }
910
- async load({ tableName, keys }) {
911
- const key = getKey(tableName, keys);
912
- try {
913
- const data = await this.client.get(key);
914
- return data || null;
1054
+ createdAt: now,
1055
+ updatedAt: now
1056
+ };
1057
+ const writePipeline = this.client.pipeline();
1058
+ const threadKey = getKey(TABLE_THREADS, { id: newThreadId });
1059
+ writePipeline.set(threadKey, processRecord(TABLE_THREADS, newThread).processedRecord);
1060
+ const clonedMessages = [];
1061
+ const targetResourceId = resourceId || sourceThread.resourceId;
1062
+ const newThreadMessagesKey = getThreadMessagesKey(newThreadId);
1063
+ for (let i = 0; i < sourceMessages.length; i++) {
1064
+ const sourceMsg = sourceMessages[i];
1065
+ const newMessageId = crypto.randomUUID();
1066
+ const { _index, ...restMsg } = sourceMsg;
1067
+ const newMessage = {
1068
+ ...restMsg,
1069
+ id: newMessageId,
1070
+ threadId: newThreadId,
1071
+ resourceId: targetResourceId
1072
+ };
1073
+ const messageKey = getMessageKey(newThreadId, newMessageId);
1074
+ writePipeline.set(messageKey, newMessage);
1075
+ writePipeline.set(getMessageIndexKey(newMessageId), newThreadId);
1076
+ writePipeline.zadd(newThreadMessagesKey, {
1077
+ score: i,
1078
+ member: newMessageId
1079
+ });
1080
+ clonedMessages.push(newMessage);
1081
+ }
1082
+ await writePipeline.exec();
1083
+ return {
1084
+ thread: newThread,
1085
+ clonedMessages
1086
+ };
915
1087
  } catch (error) {
1088
+ if (error instanceof MastraError) {
1089
+ throw error;
1090
+ }
916
1091
  throw new MastraError(
917
1092
  {
918
- id: "STORAGE_UPSTASH_STORAGE_LOAD_FAILED",
1093
+ id: createStorageErrorId("UPSTASH", "CLONE_THREAD", "FAILED"),
919
1094
  domain: ErrorDomain.STORAGE,
920
1095
  category: ErrorCategory.THIRD_PARTY,
921
- details: {
922
- tableName
923
- }
1096
+ details: { sourceThreadId, newThreadId }
924
1097
  },
925
1098
  error
926
1099
  );
927
1100
  }
928
1101
  }
929
- async hasColumn(_tableName, _column) {
930
- return true;
931
- }
932
- async scanKeys(pattern, batchSize = 1e4) {
933
- let cursor = "0";
934
- let keys = [];
935
- do {
936
- const [nextCursor, batch] = await this.client.scan(cursor, {
937
- match: pattern,
938
- count: batchSize
939
- });
940
- keys.push(...batch);
941
- cursor = nextCursor;
942
- } while (cursor !== "0");
943
- return keys;
944
- }
945
- async scanAndDelete(pattern, batchSize = 1e4) {
946
- let cursor = "0";
947
- let totalDeleted = 0;
948
- do {
949
- const [nextCursor, keys] = await this.client.scan(cursor, {
950
- match: pattern,
951
- count: batchSize
952
- });
953
- if (keys.length > 0) {
954
- await this.client.del(...keys);
955
- totalDeleted += keys.length;
956
- }
957
- cursor = nextCursor;
958
- } while (cursor !== "0");
959
- return totalDeleted;
960
- }
961
1102
  };
962
1103
  function transformScoreRow(row) {
963
- const parseField = (v) => {
964
- if (typeof v === "string") {
965
- try {
966
- return JSON.parse(v);
967
- } catch {
968
- return v;
969
- }
970
- }
971
- return v;
972
- };
973
- return {
974
- ...row,
975
- scorer: parseField(row.scorer),
976
- preprocessStepResult: parseField(row.preprocessStepResult),
977
- generateScorePrompt: row.generateScorePrompt,
978
- generateReasonPrompt: row.generateReasonPrompt,
979
- analyzeStepResult: parseField(row.analyzeStepResult),
980
- metadata: parseField(row.metadata),
981
- input: parseField(row.input),
982
- output: parseField(row.output),
983
- additionalContext: parseField(row.additionalContext),
984
- requestContext: parseField(row.requestContext),
985
- entity: parseField(row.entity),
986
- createdAt: row.createdAt,
987
- updatedAt: row.updatedAt
988
- };
1104
+ return transformScoreRow$1(row);
989
1105
  }
990
1106
  var ScoresUpstash = class extends ScoresStorage {
991
1107
  client;
992
- operations;
993
- constructor({ client, operations }) {
1108
+ #db;
1109
+ constructor(config) {
994
1110
  super();
1111
+ const client = resolveUpstashConfig(config);
995
1112
  this.client = client;
996
- this.operations = operations;
1113
+ this.#db = new UpstashDB({ client });
1114
+ }
1115
+ async dangerouslyClearAll() {
1116
+ await this.#db.deleteData({ tableName: TABLE_SCORERS });
997
1117
  }
998
1118
  async getScoreById({ id }) {
999
1119
  try {
1000
- const data = await this.operations.load({
1120
+ const data = await this.#db.get({
1001
1121
  tableName: TABLE_SCORERS,
1002
1122
  keys: { id }
1003
1123
  });
@@ -1006,10 +1126,12 @@ var ScoresUpstash = class extends ScoresStorage {
1006
1126
  } catch (error) {
1007
1127
  throw new MastraError(
1008
1128
  {
1009
- id: "STORAGE_UPSTASH_STORAGE_GET_SCORE_BY_ID_FAILED",
1129
+ id: createStorageErrorId("UPSTASH", "GET_SCORE_BY_ID", "FAILED"),
1010
1130
  domain: ErrorDomain.STORAGE,
1011
1131
  category: ErrorCategory.THIRD_PARTY,
1012
- details: { id }
1132
+ details: {
1133
+ ...id && { id }
1134
+ }
1013
1135
  },
1014
1136
  error
1015
1137
  );
@@ -1023,7 +1145,7 @@ var ScoresUpstash = class extends ScoresStorage {
1023
1145
  pagination = { page: 0, perPage: 20 }
1024
1146
  }) {
1025
1147
  const pattern = `${TABLE_SCORERS}:*`;
1026
- const keys = await this.operations.scanKeys(pattern);
1148
+ const keys = await this.#db.scanKeys(pattern);
1027
1149
  const { page, perPage: perPageInput } = pagination;
1028
1150
  if (keys.length === 0) {
1029
1151
  return {
@@ -1075,24 +1197,41 @@ var ScoresUpstash = class extends ScoresStorage {
1075
1197
  } catch (error) {
1076
1198
  throw new MastraError(
1077
1199
  {
1078
- id: "STORAGE_UPSTASH_STORAGE_SAVE_SCORE_VALIDATION_FAILED",
1200
+ id: createStorageErrorId("UPSTASH", "SAVE_SCORE", "VALIDATION_FAILED"),
1079
1201
  domain: ErrorDomain.STORAGE,
1080
- category: ErrorCategory.THIRD_PARTY
1202
+ category: ErrorCategory.USER,
1203
+ details: {
1204
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
1205
+ entityId: score.entityId ?? "unknown",
1206
+ entityType: score.entityType ?? "unknown",
1207
+ traceId: score.traceId ?? "",
1208
+ spanId: score.spanId ?? ""
1209
+ }
1081
1210
  },
1082
1211
  error
1083
1212
  );
1084
1213
  }
1085
- const { key, processedRecord } = processRecord(TABLE_SCORERS, validatedScore);
1214
+ const now = /* @__PURE__ */ new Date();
1215
+ const id = crypto.randomUUID();
1216
+ const createdAt = now;
1217
+ const updatedAt = now;
1218
+ const scoreWithId = {
1219
+ ...validatedScore,
1220
+ id,
1221
+ createdAt,
1222
+ updatedAt
1223
+ };
1224
+ const { key, processedRecord } = processRecord(TABLE_SCORERS, scoreWithId);
1086
1225
  try {
1087
1226
  await this.client.set(key, processedRecord);
1088
- return { score };
1227
+ return { score: { ...validatedScore, id, createdAt, updatedAt } };
1089
1228
  } catch (error) {
1090
1229
  throw new MastraError(
1091
1230
  {
1092
- id: "STORAGE_UPSTASH_STORAGE_SAVE_SCORE_FAILED",
1231
+ id: createStorageErrorId("UPSTASH", "SAVE_SCORE", "FAILED"),
1093
1232
  domain: ErrorDomain.STORAGE,
1094
1233
  category: ErrorCategory.THIRD_PARTY,
1095
- details: { id: score.id }
1234
+ details: { id }
1096
1235
  },
1097
1236
  error
1098
1237
  );
@@ -1103,7 +1242,7 @@ var ScoresUpstash = class extends ScoresStorage {
1103
1242
  pagination = { page: 0, perPage: 20 }
1104
1243
  }) {
1105
1244
  const pattern = `${TABLE_SCORERS}:*`;
1106
- const keys = await this.operations.scanKeys(pattern);
1245
+ const keys = await this.#db.scanKeys(pattern);
1107
1246
  const { page, perPage: perPageInput } = pagination;
1108
1247
  if (keys.length === 0) {
1109
1248
  return {
@@ -1147,7 +1286,7 @@ var ScoresUpstash = class extends ScoresStorage {
1147
1286
  pagination = { page: 0, perPage: 20 }
1148
1287
  }) {
1149
1288
  const pattern = `${TABLE_SCORERS}:*`;
1150
- const keys = await this.operations.scanKeys(pattern);
1289
+ const keys = await this.#db.scanKeys(pattern);
1151
1290
  const { page, perPage: perPageInput } = pagination;
1152
1291
  if (keys.length === 0) {
1153
1292
  return {
@@ -1196,7 +1335,7 @@ var ScoresUpstash = class extends ScoresStorage {
1196
1335
  pagination = { page: 0, perPage: 20 }
1197
1336
  }) {
1198
1337
  const pattern = `${TABLE_SCORERS}:*`;
1199
- const keys = await this.operations.scanKeys(pattern);
1338
+ const keys = await this.#db.scanKeys(pattern);
1200
1339
  const { page, perPage: perPageInput } = pagination;
1201
1340
  if (keys.length === 0) {
1202
1341
  return {
@@ -1260,32 +1399,109 @@ function parseWorkflowRun(row) {
1260
1399
  }
1261
1400
  var WorkflowsUpstash = class extends WorkflowsStorage {
1262
1401
  client;
1263
- operations;
1264
- constructor({ client, operations }) {
1402
+ #db;
1403
+ constructor(config) {
1265
1404
  super();
1405
+ const client = resolveUpstashConfig(config);
1266
1406
  this.client = client;
1267
- this.operations = operations;
1268
- }
1269
- updateWorkflowResults({
1270
- // workflowName,
1271
- // runId,
1272
- // stepId,
1273
- // result,
1274
- // requestContext,
1407
+ this.#db = new UpstashDB({ client });
1408
+ }
1409
+ async dangerouslyClearAll() {
1410
+ await this.#db.deleteData({ tableName: TABLE_WORKFLOW_SNAPSHOT });
1411
+ }
1412
+ async updateWorkflowResults({
1413
+ workflowName,
1414
+ runId,
1415
+ stepId,
1416
+ result,
1417
+ requestContext
1275
1418
  }) {
1276
- throw new Error("Method not implemented.");
1419
+ try {
1420
+ const existingSnapshot = await this.loadWorkflowSnapshot({
1421
+ namespace: "workflows",
1422
+ workflowName,
1423
+ runId
1424
+ });
1425
+ let snapshot;
1426
+ if (!existingSnapshot) {
1427
+ snapshot = {
1428
+ context: {},
1429
+ activePaths: [],
1430
+ timestamp: Date.now(),
1431
+ suspendedPaths: {},
1432
+ activeStepsPath: {},
1433
+ resumeLabels: {},
1434
+ serializedStepGraph: [],
1435
+ status: "pending",
1436
+ value: {},
1437
+ waitingPaths: {},
1438
+ runId,
1439
+ requestContext: {}
1440
+ };
1441
+ } else {
1442
+ snapshot = existingSnapshot;
1443
+ }
1444
+ snapshot.context[stepId] = result;
1445
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
1446
+ await this.persistWorkflowSnapshot({
1447
+ namespace: "workflows",
1448
+ workflowName,
1449
+ runId,
1450
+ snapshot
1451
+ });
1452
+ return snapshot.context;
1453
+ } catch (error) {
1454
+ if (error instanceof MastraError) throw error;
1455
+ throw new MastraError(
1456
+ {
1457
+ id: createStorageErrorId("UPSTASH", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
1458
+ domain: ErrorDomain.STORAGE,
1459
+ category: ErrorCategory.THIRD_PARTY,
1460
+ details: { workflowName, runId, stepId }
1461
+ },
1462
+ error
1463
+ );
1464
+ }
1277
1465
  }
1278
- updateWorkflowState({
1279
- // workflowName,
1280
- // runId,
1281
- // opts,
1466
+ async updateWorkflowState({
1467
+ workflowName,
1468
+ runId,
1469
+ opts
1282
1470
  }) {
1283
- throw new Error("Method not implemented.");
1471
+ try {
1472
+ const existingSnapshot = await this.loadWorkflowSnapshot({
1473
+ namespace: "workflows",
1474
+ workflowName,
1475
+ runId
1476
+ });
1477
+ if (!existingSnapshot || !existingSnapshot.context) {
1478
+ return void 0;
1479
+ }
1480
+ const updatedSnapshot = { ...existingSnapshot, ...opts };
1481
+ await this.persistWorkflowSnapshot({
1482
+ namespace: "workflows",
1483
+ workflowName,
1484
+ runId,
1485
+ snapshot: updatedSnapshot
1486
+ });
1487
+ return updatedSnapshot;
1488
+ } catch (error) {
1489
+ if (error instanceof MastraError) throw error;
1490
+ throw new MastraError(
1491
+ {
1492
+ id: createStorageErrorId("UPSTASH", "UPDATE_WORKFLOW_STATE", "FAILED"),
1493
+ domain: ErrorDomain.STORAGE,
1494
+ category: ErrorCategory.THIRD_PARTY,
1495
+ details: { workflowName, runId }
1496
+ },
1497
+ error
1498
+ );
1499
+ }
1284
1500
  }
1285
1501
  async persistWorkflowSnapshot(params) {
1286
- const { namespace = "workflows", workflowName, runId, resourceId, snapshot } = params;
1502
+ const { namespace = "workflows", workflowName, runId, resourceId, snapshot, createdAt, updatedAt } = params;
1287
1503
  try {
1288
- await this.operations.insert({
1504
+ await this.#db.insert({
1289
1505
  tableName: TABLE_WORKFLOW_SNAPSHOT,
1290
1506
  record: {
1291
1507
  namespace,
@@ -1293,14 +1509,14 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1293
1509
  run_id: runId,
1294
1510
  resourceId,
1295
1511
  snapshot,
1296
- createdAt: /* @__PURE__ */ new Date(),
1297
- updatedAt: /* @__PURE__ */ new Date()
1512
+ createdAt: createdAt ?? /* @__PURE__ */ new Date(),
1513
+ updatedAt: updatedAt ?? /* @__PURE__ */ new Date()
1298
1514
  }
1299
1515
  });
1300
1516
  } catch (error) {
1301
1517
  throw new MastraError(
1302
1518
  {
1303
- id: "STORAGE_UPSTASH_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1519
+ id: createStorageErrorId("UPSTASH", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
1304
1520
  domain: ErrorDomain.STORAGE,
1305
1521
  category: ErrorCategory.THIRD_PARTY,
1306
1522
  details: {
@@ -1327,7 +1543,7 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1327
1543
  } catch (error) {
1328
1544
  throw new MastraError(
1329
1545
  {
1330
- id: "STORAGE_UPSTASH_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1546
+ id: createStorageErrorId("UPSTASH", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
1331
1547
  domain: ErrorDomain.STORAGE,
1332
1548
  category: ErrorCategory.THIRD_PARTY,
1333
1549
  details: {
@@ -1346,7 +1562,7 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1346
1562
  }) {
1347
1563
  try {
1348
1564
  const key = getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName, run_id: runId }) + "*";
1349
- const keys = await this.operations.scanKeys(key);
1565
+ const keys = await this.#db.scanKeys(key);
1350
1566
  const workflows = await Promise.all(
1351
1567
  keys.map(async (key2) => {
1352
1568
  const data2 = await this.client.get(key2);
@@ -1359,7 +1575,7 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1359
1575
  } catch (error) {
1360
1576
  throw new MastraError(
1361
1577
  {
1362
- id: "STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1578
+ id: createStorageErrorId("UPSTASH", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
1363
1579
  domain: ErrorDomain.STORAGE,
1364
1580
  category: ErrorCategory.THIRD_PARTY,
1365
1581
  details: {
@@ -1372,19 +1588,40 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1372
1588
  );
1373
1589
  }
1374
1590
  }
1591
+ async deleteWorkflowRunById({ runId, workflowName }) {
1592
+ const key = getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName, run_id: runId });
1593
+ try {
1594
+ await this.client.del(key);
1595
+ } catch (error) {
1596
+ throw new MastraError(
1597
+ {
1598
+ id: createStorageErrorId("UPSTASH", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
1599
+ domain: ErrorDomain.STORAGE,
1600
+ category: ErrorCategory.THIRD_PARTY,
1601
+ details: {
1602
+ namespace: "workflows",
1603
+ runId,
1604
+ workflowName
1605
+ }
1606
+ },
1607
+ error
1608
+ );
1609
+ }
1610
+ }
1375
1611
  async listWorkflowRuns({
1376
1612
  workflowName,
1377
1613
  fromDate,
1378
1614
  toDate,
1379
1615
  perPage,
1380
1616
  page,
1381
- resourceId
1382
- }) {
1617
+ resourceId,
1618
+ status
1619
+ } = {}) {
1383
1620
  try {
1384
1621
  if (page !== void 0 && page < 0) {
1385
1622
  throw new MastraError(
1386
1623
  {
1387
- id: "UPSTASH_STORE_INVALID_PAGE",
1624
+ id: createStorageErrorId("UPSTASH", "LIST_WORKFLOW_RUNS", "INVALID_PAGE"),
1388
1625
  domain: ErrorDomain.STORAGE,
1389
1626
  category: ErrorCategory.USER,
1390
1627
  details: { page }
@@ -1410,7 +1647,7 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1410
1647
  resourceId
1411
1648
  });
1412
1649
  }
1413
- const keys = await this.operations.scanKeys(pattern);
1650
+ const keys = await this.#db.scanKeys(pattern);
1414
1651
  if (keys.length === 0) {
1415
1652
  return { runs: [], total: 0 };
1416
1653
  }
@@ -1422,6 +1659,18 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1422
1659
  ).filter((record) => !workflowName || record.workflow_name === workflowName).map((w) => parseWorkflowRun(w)).filter((w) => {
1423
1660
  if (fromDate && w.createdAt < fromDate) return false;
1424
1661
  if (toDate && w.createdAt > toDate) return false;
1662
+ if (status) {
1663
+ let snapshot = w.snapshot;
1664
+ if (typeof snapshot === "string") {
1665
+ try {
1666
+ snapshot = JSON.parse(snapshot);
1667
+ } catch (e) {
1668
+ console.warn(`Failed to parse snapshot for workflow ${w.workflowName}: ${e}`);
1669
+ return false;
1670
+ }
1671
+ }
1672
+ return snapshot.status === status;
1673
+ }
1425
1674
  return true;
1426
1675
  }).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
1427
1676
  const total = runs.length;
@@ -1432,9 +1681,10 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1432
1681
  }
1433
1682
  return { runs, total };
1434
1683
  } catch (error) {
1684
+ if (error instanceof MastraError) throw error;
1435
1685
  throw new MastraError(
1436
1686
  {
1437
- id: "STORAGE_UPSTASH_STORAGE_LIST_WORKFLOW_RUNS_FAILED",
1687
+ id: createStorageErrorId("UPSTASH", "LIST_WORKFLOW_RUNS", "FAILED"),
1438
1688
  domain: ErrorDomain.STORAGE,
1439
1689
  category: ErrorCategory.THIRD_PARTY,
1440
1690
  details: {
@@ -1450,186 +1700,39 @@ var WorkflowsUpstash = class extends WorkflowsStorage {
1450
1700
  };
1451
1701
 
1452
1702
  // src/storage/index.ts
1703
+ var isClientConfig = (config) => {
1704
+ return "client" in config;
1705
+ };
1453
1706
  var UpstashStore = class extends MastraStorage {
1454
1707
  redis;
1455
1708
  stores;
1456
1709
  constructor(config) {
1457
- super({ id: config.id, name: "Upstash" });
1458
- this.redis = new Redis({
1459
- url: config.url,
1460
- token: config.token
1461
- });
1462
- const operations = new StoreOperationsUpstash({ client: this.redis });
1463
- const scores = new ScoresUpstash({ client: this.redis, operations });
1464
- const workflows = new WorkflowsUpstash({ client: this.redis, operations });
1465
- const memory = new StoreMemoryUpstash({ client: this.redis, operations });
1710
+ super({ id: config.id, name: "Upstash", disableInit: config.disableInit });
1711
+ if (isClientConfig(config)) {
1712
+ this.redis = config.client;
1713
+ } else {
1714
+ if (!config.url || typeof config.url !== "string" || config.url.trim() === "") {
1715
+ throw new Error("UpstashStore: url is required and cannot be empty.");
1716
+ }
1717
+ if (!config.token || typeof config.token !== "string" || config.token.trim() === "") {
1718
+ throw new Error("UpstashStore: token is required and cannot be empty.");
1719
+ }
1720
+ this.redis = new Redis({
1721
+ url: config.url,
1722
+ token: config.token
1723
+ });
1724
+ }
1725
+ const scores = new ScoresUpstash({ client: this.redis });
1726
+ const workflows = new WorkflowsUpstash({ client: this.redis });
1727
+ const memory = new StoreMemoryUpstash({ client: this.redis });
1466
1728
  this.stores = {
1467
- operations,
1468
1729
  scores,
1469
1730
  workflows,
1470
1731
  memory
1471
1732
  };
1472
1733
  }
1473
- get supports() {
1474
- return {
1475
- selectByIncludeResourceScope: true,
1476
- resourceWorkingMemory: true,
1477
- hasColumn: false,
1478
- createTable: false,
1479
- deleteMessages: true,
1480
- listScoresBySpan: true
1481
- };
1482
- }
1483
- async createTable({
1484
- tableName,
1485
- schema
1486
- }) {
1487
- return this.stores.operations.createTable({ tableName, schema });
1488
- }
1489
- /**
1490
- * No-op: This backend is schemaless and does not require schema changes.
1491
- * @param tableName Name of the table
1492
- * @param schema Schema of the table
1493
- * @param ifNotExists Array of column names to add if they don't exist
1494
- */
1495
- async alterTable(args) {
1496
- return this.stores.operations.alterTable(args);
1497
- }
1498
- async clearTable({ tableName }) {
1499
- return this.stores.operations.clearTable({ tableName });
1500
- }
1501
- async dropTable({ tableName }) {
1502
- return this.stores.operations.dropTable({ tableName });
1503
- }
1504
- async insert({ tableName, record }) {
1505
- return this.stores.operations.insert({ tableName, record });
1506
- }
1507
- async batchInsert(input) {
1508
- return this.stores.operations.batchInsert(input);
1509
- }
1510
- async load({ tableName, keys }) {
1511
- return this.stores.operations.load({ tableName, keys });
1512
- }
1513
- async getThreadById({ threadId }) {
1514
- return this.stores.memory.getThreadById({ threadId });
1515
- }
1516
- async saveThread({ thread }) {
1517
- return this.stores.memory.saveThread({ thread });
1518
- }
1519
- async updateThread({
1520
- id,
1521
- title,
1522
- metadata
1523
- }) {
1524
- return this.stores.memory.updateThread({ id, title, metadata });
1525
- }
1526
- async deleteThread({ threadId }) {
1527
- return this.stores.memory.deleteThread({ threadId });
1528
- }
1529
- async saveMessages(args) {
1530
- return this.stores.memory.saveMessages(args);
1531
- }
1532
- async listMessagesById({ messageIds }) {
1533
- return this.stores.memory.listMessagesById({ messageIds });
1534
- }
1535
- async updateWorkflowResults({
1536
- workflowName,
1537
- runId,
1538
- stepId,
1539
- result,
1540
- requestContext
1541
- }) {
1542
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
1543
- }
1544
- async updateWorkflowState({
1545
- workflowName,
1546
- runId,
1547
- opts
1548
- }) {
1549
- return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
1550
- }
1551
- async persistWorkflowSnapshot(params) {
1552
- return this.stores.workflows.persistWorkflowSnapshot(params);
1553
- }
1554
- async loadWorkflowSnapshot(params) {
1555
- return this.stores.workflows.loadWorkflowSnapshot(params);
1556
- }
1557
- async listWorkflowRuns({
1558
- workflowName,
1559
- fromDate,
1560
- toDate,
1561
- perPage,
1562
- page,
1563
- resourceId
1564
- } = {}) {
1565
- return this.stores.workflows.listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId });
1566
- }
1567
- async getWorkflowRunById({
1568
- runId,
1569
- workflowName
1570
- }) {
1571
- return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
1572
- }
1573
1734
  async close() {
1574
1735
  }
1575
- async updateMessages(args) {
1576
- return this.stores.memory.updateMessages(args);
1577
- }
1578
- async deleteMessages(messageIds) {
1579
- return this.stores.memory.deleteMessages(messageIds);
1580
- }
1581
- async getResourceById({ resourceId }) {
1582
- return this.stores.memory.getResourceById({ resourceId });
1583
- }
1584
- async saveResource({ resource }) {
1585
- return this.stores.memory.saveResource({ resource });
1586
- }
1587
- async updateResource({
1588
- resourceId,
1589
- workingMemory,
1590
- metadata
1591
- }) {
1592
- return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
1593
- }
1594
- async getScoreById({ id: _id }) {
1595
- return this.stores.scores.getScoreById({ id: _id });
1596
- }
1597
- async saveScore(score) {
1598
- return this.stores.scores.saveScore(score);
1599
- }
1600
- async listScoresByRunId({
1601
- runId,
1602
- pagination
1603
- }) {
1604
- return this.stores.scores.listScoresByRunId({ runId, pagination });
1605
- }
1606
- async listScoresByEntityId({
1607
- entityId,
1608
- entityType,
1609
- pagination
1610
- }) {
1611
- return this.stores.scores.listScoresByEntityId({
1612
- entityId,
1613
- entityType,
1614
- pagination
1615
- });
1616
- }
1617
- async listScoresByScorerId({
1618
- scorerId,
1619
- pagination,
1620
- entityId,
1621
- entityType,
1622
- source
1623
- }) {
1624
- return this.stores.scores.listScoresByScorerId({ scorerId, pagination, entityId, entityType, source });
1625
- }
1626
- async listScoresBySpan({
1627
- traceId,
1628
- spanId,
1629
- pagination
1630
- }) {
1631
- return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
1632
- }
1633
1736
  };
1634
1737
  var UpstashFilterTranslator = class extends BaseFilterTranslator {
1635
1738
  getSupportedOperators() {
@@ -1867,7 +1970,7 @@ var UpstashVector = class extends MastraVector {
1867
1970
  } catch (error) {
1868
1971
  throw new MastraError(
1869
1972
  {
1870
- id: "STORAGE_UPSTASH_VECTOR_UPSERT_FAILED",
1973
+ id: createVectorErrorId("UPSTASH", "UPSERT", "FAILED"),
1871
1974
  domain: ErrorDomain.STORAGE,
1872
1975
  category: ErrorCategory.THIRD_PARTY,
1873
1976
  details: { namespace, vectorCount: vectors.length }
@@ -1930,7 +2033,7 @@ var UpstashVector = class extends MastraVector {
1930
2033
  } catch (error) {
1931
2034
  throw new MastraError(
1932
2035
  {
1933
- id: "STORAGE_UPSTASH_VECTOR_QUERY_FAILED",
2036
+ id: createVectorErrorId("UPSTASH", "QUERY", "FAILED"),
1934
2037
  domain: ErrorDomain.STORAGE,
1935
2038
  category: ErrorCategory.THIRD_PARTY,
1936
2039
  details: { namespace, topK }
@@ -1950,7 +2053,7 @@ var UpstashVector = class extends MastraVector {
1950
2053
  } catch (error) {
1951
2054
  throw new MastraError(
1952
2055
  {
1953
- id: "STORAGE_UPSTASH_VECTOR_LIST_INDEXES_FAILED",
2056
+ id: createVectorErrorId("UPSTASH", "LIST_INDEXES", "FAILED"),
1954
2057
  domain: ErrorDomain.STORAGE,
1955
2058
  category: ErrorCategory.THIRD_PARTY
1956
2059
  },
@@ -1975,7 +2078,7 @@ var UpstashVector = class extends MastraVector {
1975
2078
  } catch (error) {
1976
2079
  throw new MastraError(
1977
2080
  {
1978
- id: "STORAGE_UPSTASH_VECTOR_DESCRIBE_INDEX_FAILED",
2081
+ id: createVectorErrorId("UPSTASH", "DESCRIBE_INDEX", "FAILED"),
1979
2082
  domain: ErrorDomain.STORAGE,
1980
2083
  category: ErrorCategory.THIRD_PARTY,
1981
2084
  details: { namespace }
@@ -1993,9 +2096,14 @@ var UpstashVector = class extends MastraVector {
1993
2096
  try {
1994
2097
  await this.client.deleteNamespace(namespace);
1995
2098
  } catch (error) {
2099
+ const errorMessage = error?.message || "";
2100
+ if (errorMessage.includes("does not exist") || errorMessage.includes("not found")) {
2101
+ this.logger.info(`Namespace ${namespace} does not exist, treating as already deleted`);
2102
+ return;
2103
+ }
1996
2104
  throw new MastraError(
1997
2105
  {
1998
- id: "STORAGE_UPSTASH_VECTOR_DELETE_INDEX_FAILED",
2106
+ id: createVectorErrorId("UPSTASH", "DELETE_INDEX", "FAILED"),
1999
2107
  domain: ErrorDomain.STORAGE,
2000
2108
  category: ErrorCategory.THIRD_PARTY,
2001
2109
  details: { namespace }
@@ -2005,47 +2113,124 @@ var UpstashVector = class extends MastraVector {
2005
2113
  }
2006
2114
  }
2007
2115
  /**
2008
- * Updates a vector by its ID with the provided vector and/or metadata.
2009
- * @param indexName - The name of the namespace containing the vector.
2010
- * @param id - The ID of the vector to update.
2011
- * @param update - An object containing the vector and/or metadata to update.
2012
- * @param update.vector - An optional array of numbers representing the new vector.
2013
- * @param update.metadata - An optional record containing the new metadata.
2116
+ * Updates a vector by its ID or multiple vectors matching a filter.
2117
+ * @param params - Parameters containing the id or filter for targeting the vector(s) to update
2118
+ * @param params.indexName - The name of the namespace containing the vector.
2119
+ * @param params.id - The ID of the vector to update (mutually exclusive with filter).
2120
+ * @param params.filter - Filter to match multiple vectors to update (mutually exclusive with id).
2121
+ * @param params.update - An object containing the vector and/or metadata to update.
2014
2122
  * @returns A promise that resolves when the update is complete.
2015
2123
  * @throws Will throw an error if no updates are provided or if the update operation fails.
2016
2124
  */
2017
- async updateVector({ indexName: namespace, id, update }) {
2018
- if (!update.vector && !update.metadata && !update.sparseVector) {
2125
+ async updateVector(params) {
2126
+ const { indexName: namespace, update } = params;
2127
+ const upstashUpdate = update;
2128
+ const sparseVector = upstashUpdate.sparseVector;
2129
+ if ("id" in params && params.id && "filter" in params && params.filter) {
2130
+ throw new MastraError({
2131
+ id: createVectorErrorId("UPSTASH", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
2132
+ text: "Cannot specify both id and filter - they are mutually exclusive",
2133
+ domain: ErrorDomain.STORAGE,
2134
+ category: ErrorCategory.USER,
2135
+ details: { namespace }
2136
+ });
2137
+ }
2138
+ if (!("id" in params && params.id) && !("filter" in params && params.filter)) {
2139
+ throw new MastraError({
2140
+ id: createVectorErrorId("UPSTASH", "UPDATE_VECTOR", "NO_TARGET"),
2141
+ text: "Either id or filter must be provided",
2142
+ domain: ErrorDomain.STORAGE,
2143
+ category: ErrorCategory.USER,
2144
+ details: { namespace }
2145
+ });
2146
+ }
2147
+ if (!update.vector && !update.metadata && !sparseVector) {
2019
2148
  throw new MastraError({
2020
- id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2149
+ id: createVectorErrorId("UPSTASH", "UPDATE_VECTOR", "NO_PAYLOAD"),
2150
+ text: "No update data provided",
2021
2151
  domain: ErrorDomain.STORAGE,
2022
- category: ErrorCategory.THIRD_PARTY,
2023
- details: { namespace, id },
2024
- text: "No update data provided"
2152
+ category: ErrorCategory.USER,
2153
+ details: { namespace }
2025
2154
  });
2026
2155
  }
2027
- if (!update.vector && !update.sparseVector && update.metadata) {
2156
+ if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
2028
2157
  throw new MastraError({
2029
- id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2158
+ id: createVectorErrorId("UPSTASH", "UPDATE_VECTOR", "EMPTY_FILTER"),
2159
+ text: "Filter cannot be an empty filter object",
2030
2160
  domain: ErrorDomain.STORAGE,
2031
- category: ErrorCategory.THIRD_PARTY,
2032
- details: { namespace, id },
2033
- text: "Both vector and metadata must be provided for an update"
2161
+ category: ErrorCategory.USER,
2162
+ details: { namespace }
2034
2163
  });
2035
2164
  }
2036
2165
  try {
2037
- const points = { id };
2038
- if (update.vector) points.vector = update.vector;
2039
- if (update.metadata) points.metadata = update.metadata;
2040
- if (update.sparseVector) points.sparseVector = update.sparseVector;
2041
- await this.client.upsert(points, { namespace });
2166
+ const ns = this.client.namespace(namespace);
2167
+ if ("id" in params && params.id) {
2168
+ const points = { id: params.id };
2169
+ if (!update.vector || !update.metadata) {
2170
+ try {
2171
+ const existing = await ns.fetch([params.id], {
2172
+ includeVectors: true,
2173
+ includeMetadata: true
2174
+ });
2175
+ if (existing && existing.length > 0 && existing[0]) {
2176
+ if (!update.vector && existing[0]?.vector) {
2177
+ points.vector = existing[0].vector;
2178
+ }
2179
+ if (!update.metadata && existing[0]?.metadata) {
2180
+ points.metadata = existing[0].metadata;
2181
+ }
2182
+ }
2183
+ } catch (fetchError) {
2184
+ this.logger.warn(`Failed to fetch existing vector ${params.id} for partial update: ${fetchError}`);
2185
+ }
2186
+ }
2187
+ if (update.vector) points.vector = update.vector;
2188
+ if (update.metadata) points.metadata = update.metadata;
2189
+ if (sparseVector) points.sparseVector = sparseVector;
2190
+ await ns.upsert(points);
2191
+ } else if ("filter" in params && params.filter) {
2192
+ const filterString = this.transformFilter(params.filter);
2193
+ if (filterString) {
2194
+ const stats = await this.describeIndex({ indexName: namespace });
2195
+ const dummyVector = new Array(stats.dimension).fill(1 / Math.sqrt(stats.dimension));
2196
+ const needsVectors = !update.vector;
2197
+ const results = await ns.query({
2198
+ vector: dummyVector,
2199
+ topK: 1e3,
2200
+ // Upstash's max query limit
2201
+ filter: filterString,
2202
+ includeVectors: needsVectors,
2203
+ includeMetadata: needsVectors
2204
+ });
2205
+ for (const result of results) {
2206
+ const points = { id: `${result.id}` };
2207
+ if (update.vector) {
2208
+ points.vector = update.vector;
2209
+ } else if (result.vector) {
2210
+ points.vector = result.vector;
2211
+ }
2212
+ if (update.metadata) {
2213
+ points.metadata = update.metadata;
2214
+ } else if (result.metadata) {
2215
+ points.metadata = result.metadata;
2216
+ }
2217
+ if (sparseVector) points.sparseVector = sparseVector;
2218
+ await ns.upsert(points);
2219
+ }
2220
+ }
2221
+ }
2042
2222
  } catch (error) {
2223
+ if (error instanceof MastraError) throw error;
2043
2224
  throw new MastraError(
2044
2225
  {
2045
- id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2226
+ id: createVectorErrorId("UPSTASH", "UPDATE_VECTOR", "FAILED"),
2046
2227
  domain: ErrorDomain.STORAGE,
2047
2228
  category: ErrorCategory.THIRD_PARTY,
2048
- details: { namespace, id }
2229
+ details: {
2230
+ namespace,
2231
+ ..."id" in params && params.id && { id: params.id },
2232
+ ..."filter" in params && params.filter && { filter: JSON.stringify(params.filter) }
2233
+ }
2049
2234
  },
2050
2235
  error
2051
2236
  );
@@ -2060,22 +2245,109 @@ var UpstashVector = class extends MastraVector {
2060
2245
  */
2061
2246
  async deleteVector({ indexName: namespace, id }) {
2062
2247
  try {
2063
- await this.client.delete(id, {
2064
- namespace
2065
- });
2248
+ const ns = this.client.namespace(namespace);
2249
+ await ns.delete(id);
2066
2250
  } catch (error) {
2067
2251
  const mastraError = new MastraError(
2068
2252
  {
2069
- id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTOR_FAILED",
2253
+ id: createVectorErrorId("UPSTASH", "DELETE_VECTOR", "FAILED"),
2070
2254
  domain: ErrorDomain.STORAGE,
2071
2255
  category: ErrorCategory.THIRD_PARTY,
2072
- details: { namespace, id }
2256
+ details: {
2257
+ namespace,
2258
+ ...id && { id }
2259
+ }
2073
2260
  },
2074
2261
  error
2075
2262
  );
2076
2263
  this.logger?.error(mastraError.toString());
2077
2264
  }
2078
2265
  }
2266
+ /**
2267
+ * Deletes multiple vectors by IDs or filter.
2268
+ * @param indexName - The name of the namespace containing the vectors.
2269
+ * @param ids - Array of vector IDs to delete (mutually exclusive with filter).
2270
+ * @param filter - Filter to match vectors to delete (mutually exclusive with ids).
2271
+ * @returns A promise that resolves when the deletion is complete.
2272
+ * @throws Will throw an error if both ids and filter are provided, or if neither is provided.
2273
+ */
2274
+ async deleteVectors({ indexName: namespace, filter, ids }) {
2275
+ if (ids && filter) {
2276
+ throw new MastraError({
2277
+ id: createVectorErrorId("UPSTASH", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
2278
+ text: "Cannot specify both ids and filter - they are mutually exclusive",
2279
+ domain: ErrorDomain.STORAGE,
2280
+ category: ErrorCategory.USER,
2281
+ details: { namespace }
2282
+ });
2283
+ }
2284
+ if (!ids && !filter) {
2285
+ throw new MastraError({
2286
+ id: createVectorErrorId("UPSTASH", "DELETE_VECTORS", "NO_TARGET"),
2287
+ text: "Either filter or ids must be provided",
2288
+ domain: ErrorDomain.STORAGE,
2289
+ category: ErrorCategory.USER,
2290
+ details: { namespace }
2291
+ });
2292
+ }
2293
+ if (ids && ids.length === 0) {
2294
+ throw new MastraError({
2295
+ id: createVectorErrorId("UPSTASH", "DELETE_VECTORS", "EMPTY_IDS"),
2296
+ text: "Cannot delete with empty ids array",
2297
+ domain: ErrorDomain.STORAGE,
2298
+ category: ErrorCategory.USER,
2299
+ details: { namespace }
2300
+ });
2301
+ }
2302
+ if (filter && Object.keys(filter).length === 0) {
2303
+ throw new MastraError({
2304
+ id: createVectorErrorId("UPSTASH", "DELETE_VECTORS", "EMPTY_FILTER"),
2305
+ text: "Cannot delete with empty filter object",
2306
+ domain: ErrorDomain.STORAGE,
2307
+ category: ErrorCategory.USER,
2308
+ details: { namespace }
2309
+ });
2310
+ }
2311
+ try {
2312
+ const ns = this.client.namespace(namespace);
2313
+ if (ids) {
2314
+ await ns.delete(ids);
2315
+ } else if (filter) {
2316
+ const filterString = this.transformFilter(filter);
2317
+ if (filterString) {
2318
+ const stats = await this.describeIndex({ indexName: namespace });
2319
+ const dummyVector = new Array(stats.dimension).fill(1 / Math.sqrt(stats.dimension));
2320
+ const results = await ns.query({
2321
+ vector: dummyVector,
2322
+ topK: 1e3,
2323
+ // Upstash's max query limit
2324
+ filter: filterString,
2325
+ includeVectors: false,
2326
+ includeMetadata: false
2327
+ });
2328
+ const idsToDelete = results.map((r) => `${r.id}`);
2329
+ if (idsToDelete.length > 0) {
2330
+ await ns.delete(idsToDelete);
2331
+ }
2332
+ }
2333
+ }
2334
+ } catch (error) {
2335
+ if (error instanceof MastraError) throw error;
2336
+ throw new MastraError(
2337
+ {
2338
+ id: createVectorErrorId("UPSTASH", "DELETE_VECTORS", "FAILED"),
2339
+ domain: ErrorDomain.STORAGE,
2340
+ category: ErrorCategory.THIRD_PARTY,
2341
+ details: {
2342
+ namespace,
2343
+ ...filter && { filter: JSON.stringify(filter) },
2344
+ ...ids && { idsCount: ids.length }
2345
+ }
2346
+ },
2347
+ error
2348
+ );
2349
+ }
2350
+ }
2079
2351
  };
2080
2352
 
2081
2353
  // src/vector/prompt.ts
@@ -2153,6 +2425,6 @@ Example Complex Query:
2153
2425
  ]
2154
2426
  }`;
2155
2427
 
2156
- export { UPSTASH_PROMPT, UpstashStore, UpstashVector };
2428
+ export { ScoresUpstash, StoreMemoryUpstash, UPSTASH_PROMPT, UpstashStore, UpstashVector, WorkflowsUpstash };
2157
2429
  //# sourceMappingURL=index.js.map
2158
2430
  //# sourceMappingURL=index.js.map