@mastra/upstash 1.0.0-beta.1 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -54,6 +54,9 @@ function getMessageKey(threadId, messageId) {
54
54
  const key = getKey(storage.TABLE_MESSAGES, { threadId, id: messageId });
55
55
  return key;
56
56
  }
57
+ function getMessageIndexKey(messageId) {
58
+ return `msg-idx:${messageId}`;
59
+ }
57
60
  var StoreMemoryUpstash = class extends storage.MemoryStorage {
58
61
  client;
59
62
  operations;
@@ -320,6 +323,7 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
320
323
  }
321
324
  }
322
325
  pipeline.set(key, message);
326
+ pipeline.set(getMessageIndexKey(message.id), message.threadId);
323
327
  pipeline.zadd(getThreadMessagesKey(message.threadId), {
324
328
  score,
325
329
  member: message.id
@@ -350,43 +354,60 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
350
354
  );
351
355
  }
352
356
  }
353
- async _getIncludedMessages(threadId, include) {
354
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
357
+ /**
358
+ * Lookup threadId for a message - tries index first (O(1)), falls back to scan (backwards compatible)
359
+ */
360
+ async _getThreadIdForMessage(messageId) {
361
+ const indexedThreadId = await this.client.get(getMessageIndexKey(messageId));
362
+ if (indexedThreadId) {
363
+ return indexedThreadId;
364
+ }
365
+ const existingKeyPattern = getMessageKey("*", messageId);
366
+ const keys = await this.operations.scanKeys(existingKeyPattern);
367
+ if (keys.length === 0) return null;
368
+ const messageData = await this.client.get(keys[0]);
369
+ if (!messageData) return null;
370
+ if (messageData.threadId) {
371
+ await this.client.set(getMessageIndexKey(messageId), messageData.threadId);
372
+ }
373
+ return messageData.threadId || null;
374
+ }
375
+ async _getIncludedMessages(include) {
376
+ if (!include?.length) return [];
355
377
  const messageIds = /* @__PURE__ */ new Set();
356
378
  const messageIdToThreadIds = {};
357
- if (include?.length) {
358
- for (const item of include) {
359
- messageIds.add(item.id);
360
- const itemThreadId = item.threadId || threadId;
361
- messageIdToThreadIds[item.id] = itemThreadId;
362
- const itemThreadMessagesKey = getThreadMessagesKey(itemThreadId);
363
- const rank = await this.client.zrank(itemThreadMessagesKey, item.id);
364
- if (rank === null) continue;
365
- if (item.withPreviousMessages) {
366
- const start = Math.max(0, rank - item.withPreviousMessages);
367
- const prevIds = rank === 0 ? [] : await this.client.zrange(itemThreadMessagesKey, start, rank - 1);
368
- prevIds.forEach((id) => {
369
- messageIds.add(id);
370
- messageIdToThreadIds[id] = itemThreadId;
371
- });
372
- }
373
- if (item.withNextMessages) {
374
- const nextIds = await this.client.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
375
- nextIds.forEach((id) => {
376
- messageIds.add(id);
377
- messageIdToThreadIds[id] = itemThreadId;
378
- });
379
- }
379
+ for (const item of include) {
380
+ const itemThreadId = await this._getThreadIdForMessage(item.id);
381
+ if (!itemThreadId) continue;
382
+ messageIds.add(item.id);
383
+ messageIdToThreadIds[item.id] = itemThreadId;
384
+ const itemThreadMessagesKey = getThreadMessagesKey(itemThreadId);
385
+ const rank = await this.client.zrank(itemThreadMessagesKey, item.id);
386
+ if (rank === null) continue;
387
+ if (item.withPreviousMessages) {
388
+ const start = Math.max(0, rank - item.withPreviousMessages);
389
+ const prevIds = rank === 0 ? [] : await this.client.zrange(itemThreadMessagesKey, start, rank - 1);
390
+ prevIds.forEach((id) => {
391
+ messageIds.add(id);
392
+ messageIdToThreadIds[id] = itemThreadId;
393
+ });
394
+ }
395
+ if (item.withNextMessages) {
396
+ const nextIds = await this.client.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
397
+ nextIds.forEach((id) => {
398
+ messageIds.add(id);
399
+ messageIdToThreadIds[id] = itemThreadId;
400
+ });
380
401
  }
381
- const pipeline = this.client.pipeline();
382
- Array.from(messageIds).forEach((id) => {
383
- const tId = messageIdToThreadIds[id] || threadId;
384
- pipeline.get(getMessageKey(tId, id));
385
- });
386
- const results = await pipeline.exec();
387
- return results.filter((result) => result !== null);
388
402
  }
389
- return [];
403
+ if (messageIds.size === 0) return [];
404
+ const pipeline = this.client.pipeline();
405
+ Array.from(messageIds).forEach((id) => {
406
+ const tId = messageIdToThreadIds[id];
407
+ pipeline.get(getMessageKey(tId, id));
408
+ });
409
+ const results = await pipeline.exec();
410
+ return results.filter((result) => result !== null);
390
411
  }
391
412
  parseStoredMessage(storedMessage) {
392
413
  const defaultMessageContent = { format: 2, parts: [{ type: "text", text: "" }] };
@@ -400,17 +421,49 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
400
421
  async listMessagesById({ messageIds }) {
401
422
  if (messageIds.length === 0) return { messages: [] };
402
423
  try {
403
- const threadKeys = await this.client.keys("thread:*");
404
- const result = await Promise.all(
405
- threadKeys.map((threadKey) => {
406
- const threadId = threadKey.split(":")[1];
407
- if (!threadId) throw new Error(`Failed to parse thread ID from thread key "${threadKey}"`);
408
- return this.client.mget(
409
- messageIds.map((id) => getMessageKey(threadId, id))
410
- );
411
- })
412
- );
413
- const rawMessages = result.flat(1).filter((msg) => !!msg);
424
+ const rawMessages = [];
425
+ const indexPipeline = this.client.pipeline();
426
+ messageIds.forEach((id) => indexPipeline.get(getMessageIndexKey(id)));
427
+ const indexResults = await indexPipeline.exec();
428
+ const indexedIds = [];
429
+ const unindexedIds = [];
430
+ messageIds.forEach((id, i) => {
431
+ const threadId = indexResults[i];
432
+ if (threadId) {
433
+ indexedIds.push({ messageId: id, threadId });
434
+ } else {
435
+ unindexedIds.push(id);
436
+ }
437
+ });
438
+ if (indexedIds.length > 0) {
439
+ const messagePipeline = this.client.pipeline();
440
+ indexedIds.forEach(({ messageId, threadId }) => messagePipeline.get(getMessageKey(threadId, messageId)));
441
+ const messageResults = await messagePipeline.exec();
442
+ rawMessages.push(...messageResults.filter((msg) => msg !== null));
443
+ }
444
+ if (unindexedIds.length > 0) {
445
+ const threadKeys = await this.client.keys("thread:*");
446
+ const result = await Promise.all(
447
+ threadKeys.map((threadKey) => {
448
+ const threadId = threadKey.split(":")[1];
449
+ if (!threadId) throw new Error(`Failed to parse thread ID from thread key "${threadKey}"`);
450
+ return this.client.mget(
451
+ unindexedIds.map((id) => getMessageKey(threadId, id))
452
+ );
453
+ })
454
+ );
455
+ const foundMessages = result.flat(1).filter((msg) => !!msg);
456
+ rawMessages.push(...foundMessages);
457
+ if (foundMessages.length > 0) {
458
+ const backfillPipeline = this.client.pipeline();
459
+ foundMessages.forEach((msg) => {
460
+ if (msg.threadId) {
461
+ backfillPipeline.set(getMessageIndexKey(msg.id), msg.threadId);
462
+ }
463
+ });
464
+ await backfillPipeline.exec();
465
+ }
466
+ }
414
467
  const list = new agent.MessageList().add(rawMessages.map(this.parseStoredMessage), "memory");
415
468
  return { messages: list.get.all.db() };
416
469
  } catch (error$1) {
@@ -429,18 +482,18 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
429
482
  }
430
483
  async listMessages(args) {
431
484
  const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
432
- if (!threadId.trim()) {
485
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
486
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
433
487
  throw new error.MastraError(
434
488
  {
435
489
  id: "STORAGE_UPSTASH_LIST_MESSAGES_INVALID_THREAD_ID",
436
490
  domain: error.ErrorDomain.STORAGE,
437
491
  category: error.ErrorCategory.THIRD_PARTY,
438
- details: { threadId }
492
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
439
493
  },
440
- new Error("threadId must be a non-empty string")
494
+ new Error("threadId must be a non-empty string or array of non-empty strings")
441
495
  );
442
496
  }
443
- const threadMessagesKey = getThreadMessagesKey(threadId);
444
497
  const perPage = storage.normalizePerPage(perPageInput, 40);
445
498
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
446
499
  try {
@@ -457,11 +510,18 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
457
510
  }
458
511
  let includedMessages = [];
459
512
  if (include && include.length > 0) {
460
- const included = await this._getIncludedMessages(threadId, include);
513
+ const included = await this._getIncludedMessages(include);
461
514
  includedMessages = included.map(this.parseStoredMessage);
462
515
  }
463
- const allMessageIds = await this.client.zrange(threadMessagesKey, 0, -1);
464
- if (allMessageIds.length === 0) {
516
+ const allMessageIdsWithThreads = [];
517
+ for (const tid of threadIds) {
518
+ const threadMessagesKey = getThreadMessagesKey(tid);
519
+ const messageIds2 = await this.client.zrange(threadMessagesKey, 0, -1);
520
+ for (const mid of messageIds2) {
521
+ allMessageIdsWithThreads.push({ threadId: tid, messageId: mid });
522
+ }
523
+ }
524
+ if (allMessageIdsWithThreads.length === 0) {
465
525
  return {
466
526
  messages: [],
467
527
  total: 0,
@@ -471,7 +531,7 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
471
531
  };
472
532
  }
473
533
  const pipeline = this.client.pipeline();
474
- allMessageIds.forEach((id) => pipeline.get(getMessageKey(threadId, id)));
534
+ allMessageIdsWithThreads.forEach(({ threadId: tid, messageId }) => pipeline.get(getMessageKey(tid, messageId)));
475
535
  const results = await pipeline.exec();
476
536
  let messagesData = results.filter((msg) => msg !== null).map(this.parseStoredMessage);
477
537
  if (resourceId) {
@@ -500,13 +560,11 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
500
560
  }
501
561
  return 0;
502
562
  };
503
- if (orderBy) {
504
- messagesData.sort((a, b) => {
505
- const aValue = getFieldValue(a);
506
- const bValue = getFieldValue(b);
507
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
508
- });
509
- }
563
+ messagesData.sort((a, b) => {
564
+ const aValue = getFieldValue(a);
565
+ const bValue = getFieldValue(b);
566
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
567
+ });
510
568
  const total = messagesData.length;
511
569
  const start = offset;
512
570
  const end = perPageInput === false ? total : start + perPage;
@@ -527,23 +585,11 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
527
585
  }
528
586
  const list = new agent.MessageList().add(allMessages, "memory");
529
587
  let finalMessages = list.get.all.db();
530
- if (orderBy) {
531
- finalMessages = finalMessages.sort((a, b) => {
532
- const aValue = getFieldValue(a);
533
- const bValue = getFieldValue(b);
534
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
535
- });
536
- } else {
537
- const messageIdToPosition = /* @__PURE__ */ new Map();
538
- allMessageIds.forEach((id, index) => {
539
- messageIdToPosition.set(id, index);
540
- });
541
- finalMessages = finalMessages.sort((a, b) => {
542
- const aPos = messageIdToPosition.get(a.id) ?? Number.MAX_SAFE_INTEGER;
543
- const bPos = messageIdToPosition.get(b.id) ?? Number.MAX_SAFE_INTEGER;
544
- return aPos - bPos;
545
- });
546
- }
588
+ finalMessages = finalMessages.sort((a, b) => {
589
+ const aValue = getFieldValue(a);
590
+ const bValue = getFieldValue(b);
591
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
592
+ });
547
593
  const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
548
594
  const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
549
595
  const hasMore = perPageInput !== false && !allThreadMessagesReturned && end < total;
@@ -561,7 +607,7 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
561
607
  domain: error.ErrorDomain.STORAGE,
562
608
  category: error.ErrorCategory.THIRD_PARTY,
563
609
  details: {
564
- threadId,
610
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
565
611
  resourceId: resourceId ?? ""
566
612
  }
567
613
  },
@@ -766,13 +812,33 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
766
812
  try {
767
813
  const threadIds = /* @__PURE__ */ new Set();
768
814
  const messageKeys = [];
769
- for (const messageId of messageIds) {
815
+ const foundMessageIds = [];
816
+ const indexPipeline = this.client.pipeline();
817
+ messageIds.forEach((id) => indexPipeline.get(getMessageIndexKey(id)));
818
+ const indexResults = await indexPipeline.exec();
819
+ const indexedMessages = [];
820
+ const unindexedMessageIds = [];
821
+ messageIds.forEach((id, i) => {
822
+ const threadId = indexResults[i];
823
+ if (threadId) {
824
+ indexedMessages.push({ messageId: id, threadId });
825
+ } else {
826
+ unindexedMessageIds.push(id);
827
+ }
828
+ });
829
+ for (const { messageId, threadId } of indexedMessages) {
830
+ messageKeys.push(getMessageKey(threadId, messageId));
831
+ foundMessageIds.push(messageId);
832
+ threadIds.add(threadId);
833
+ }
834
+ for (const messageId of unindexedMessageIds) {
770
835
  const pattern = getMessageKey("*", messageId);
771
836
  const keys = await this.operations.scanKeys(pattern);
772
837
  for (const key of keys) {
773
838
  const message = await this.client.get(key);
774
839
  if (message && message.id === messageId) {
775
840
  messageKeys.push(key);
841
+ foundMessageIds.push(messageId);
776
842
  if (message.threadId) {
777
843
  threadIds.add(message.threadId);
778
844
  }
@@ -787,6 +853,9 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
787
853
  for (const key of messageKeys) {
788
854
  pipeline.del(key);
789
855
  }
856
+ for (const messageId of foundMessageIds) {
857
+ pipeline.del(getMessageIndexKey(messageId));
858
+ }
790
859
  if (threadIds.size > 0) {
791
860
  for (const threadId of threadIds) {
792
861
  const threadKey = getKey(storage.TABLE_THREADS, { id: threadId });
@@ -962,32 +1031,7 @@ var StoreOperationsUpstash = class extends storage.StoreOperations {
962
1031
  }
963
1032
  };
964
1033
  function transformScoreRow(row) {
965
- const parseField = (v) => {
966
- if (typeof v === "string") {
967
- try {
968
- return JSON.parse(v);
969
- } catch {
970
- return v;
971
- }
972
- }
973
- return v;
974
- };
975
- return {
976
- ...row,
977
- scorer: parseField(row.scorer),
978
- preprocessStepResult: parseField(row.preprocessStepResult),
979
- generateScorePrompt: row.generateScorePrompt,
980
- generateReasonPrompt: row.generateReasonPrompt,
981
- analyzeStepResult: parseField(row.analyzeStepResult),
982
- metadata: parseField(row.metadata),
983
- input: parseField(row.input),
984
- output: parseField(row.output),
985
- additionalContext: parseField(row.additionalContext),
986
- requestContext: parseField(row.requestContext),
987
- entity: parseField(row.entity),
988
- createdAt: row.createdAt,
989
- updatedAt: row.updatedAt
990
- };
1034
+ return storage.transformScoreRow(row);
991
1035
  }
992
1036
  var ScoresUpstash = class extends storage.ScoresStorage {
993
1037
  client;
@@ -1011,7 +1055,9 @@ var ScoresUpstash = class extends storage.ScoresStorage {
1011
1055
  id: "STORAGE_UPSTASH_STORAGE_GET_SCORE_BY_ID_FAILED",
1012
1056
  domain: error.ErrorDomain.STORAGE,
1013
1057
  category: error.ErrorCategory.THIRD_PARTY,
1014
- details: { id }
1058
+ details: {
1059
+ ...id && { id }
1060
+ }
1015
1061
  },
1016
1062
  error$1
1017
1063
  );
@@ -2001,6 +2047,11 @@ var UpstashVector = class extends vector.MastraVector {
2001
2047
  try {
2002
2048
  await this.client.deleteNamespace(namespace);
2003
2049
  } catch (error$1) {
2050
+ const errorMessage = error$1?.message || "";
2051
+ if (errorMessage.includes("does not exist") || errorMessage.includes("not found")) {
2052
+ this.logger.info(`Namespace ${namespace} does not exist, treating as already deleted`);
2053
+ return;
2054
+ }
2004
2055
  throw new error.MastraError(
2005
2056
  {
2006
2057
  id: "STORAGE_UPSTASH_VECTOR_DELETE_INDEX_FAILED",
@@ -2013,47 +2064,124 @@ var UpstashVector = class extends vector.MastraVector {
2013
2064
  }
2014
2065
  }
2015
2066
  /**
2016
- * Updates a vector by its ID with the provided vector and/or metadata.
2017
- * @param indexName - The name of the namespace containing the vector.
2018
- * @param id - The ID of the vector to update.
2019
- * @param update - An object containing the vector and/or metadata to update.
2020
- * @param update.vector - An optional array of numbers representing the new vector.
2021
- * @param update.metadata - An optional record containing the new metadata.
2067
+ * Updates a vector by its ID or multiple vectors matching a filter.
2068
+ * @param params - Parameters containing the id or filter for targeting the vector(s) to update
2069
+ * @param params.indexName - The name of the namespace containing the vector.
2070
+ * @param params.id - The ID of the vector to update (mutually exclusive with filter).
2071
+ * @param params.filter - Filter to match multiple vectors to update (mutually exclusive with id).
2072
+ * @param params.update - An object containing the vector and/or metadata to update.
2022
2073
  * @returns A promise that resolves when the update is complete.
2023
2074
  * @throws Will throw an error if no updates are provided or if the update operation fails.
2024
2075
  */
2025
- async updateVector({ indexName: namespace, id, update }) {
2026
- if (!update.vector && !update.metadata && !update.sparseVector) {
2076
+ async updateVector(params) {
2077
+ const { indexName: namespace, update } = params;
2078
+ const upstashUpdate = update;
2079
+ const sparseVector = upstashUpdate.sparseVector;
2080
+ if ("id" in params && params.id && "filter" in params && params.filter) {
2081
+ throw new error.MastraError({
2082
+ id: "STORAGE_UPSTASH_VECTOR_UPDATE_MUTUALLY_EXCLUSIVE",
2083
+ text: "Cannot specify both id and filter - they are mutually exclusive",
2084
+ domain: error.ErrorDomain.STORAGE,
2085
+ category: error.ErrorCategory.USER,
2086
+ details: { namespace }
2087
+ });
2088
+ }
2089
+ if (!("id" in params && params.id) && !("filter" in params && params.filter)) {
2027
2090
  throw new error.MastraError({
2028
- id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2091
+ id: "STORAGE_UPSTASH_VECTOR_UPDATE_NO_TARGET",
2092
+ text: "Either id or filter must be provided",
2029
2093
  domain: error.ErrorDomain.STORAGE,
2030
- category: error.ErrorCategory.THIRD_PARTY,
2031
- details: { namespace, id },
2032
- text: "No update data provided"
2094
+ category: error.ErrorCategory.USER,
2095
+ details: { namespace }
2033
2096
  });
2034
2097
  }
2035
- if (!update.vector && !update.sparseVector && update.metadata) {
2098
+ if (!update.vector && !update.metadata && !sparseVector) {
2036
2099
  throw new error.MastraError({
2037
- id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2100
+ id: "STORAGE_UPSTASH_VECTOR_UPDATE_NO_PAYLOAD",
2101
+ text: "No update data provided",
2038
2102
  domain: error.ErrorDomain.STORAGE,
2039
- category: error.ErrorCategory.THIRD_PARTY,
2040
- details: { namespace, id },
2041
- text: "Both vector and metadata must be provided for an update"
2103
+ category: error.ErrorCategory.USER,
2104
+ details: { namespace }
2105
+ });
2106
+ }
2107
+ if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
2108
+ throw new error.MastraError({
2109
+ id: "STORAGE_UPSTASH_VECTOR_UPDATE_EMPTY_FILTER",
2110
+ text: "Filter cannot be an empty filter object",
2111
+ domain: error.ErrorDomain.STORAGE,
2112
+ category: error.ErrorCategory.USER,
2113
+ details: { namespace }
2042
2114
  });
2043
2115
  }
2044
2116
  try {
2045
- const points = { id };
2046
- if (update.vector) points.vector = update.vector;
2047
- if (update.metadata) points.metadata = update.metadata;
2048
- if (update.sparseVector) points.sparseVector = update.sparseVector;
2049
- await this.client.upsert(points, { namespace });
2117
+ const ns = this.client.namespace(namespace);
2118
+ if ("id" in params && params.id) {
2119
+ const points = { id: params.id };
2120
+ if (!update.vector || !update.metadata) {
2121
+ try {
2122
+ const existing = await ns.fetch([params.id], {
2123
+ includeVectors: true,
2124
+ includeMetadata: true
2125
+ });
2126
+ if (existing && existing.length > 0 && existing[0]) {
2127
+ if (!update.vector && existing[0]?.vector) {
2128
+ points.vector = existing[0].vector;
2129
+ }
2130
+ if (!update.metadata && existing[0]?.metadata) {
2131
+ points.metadata = existing[0].metadata;
2132
+ }
2133
+ }
2134
+ } catch (fetchError) {
2135
+ this.logger.warn(`Failed to fetch existing vector ${params.id} for partial update: ${fetchError}`);
2136
+ }
2137
+ }
2138
+ if (update.vector) points.vector = update.vector;
2139
+ if (update.metadata) points.metadata = update.metadata;
2140
+ if (sparseVector) points.sparseVector = sparseVector;
2141
+ await ns.upsert(points);
2142
+ } else if ("filter" in params && params.filter) {
2143
+ const filterString = this.transformFilter(params.filter);
2144
+ if (filterString) {
2145
+ const stats = await this.describeIndex({ indexName: namespace });
2146
+ const dummyVector = new Array(stats.dimension).fill(1 / Math.sqrt(stats.dimension));
2147
+ const needsVectors = !update.vector;
2148
+ const results = await ns.query({
2149
+ vector: dummyVector,
2150
+ topK: 1e3,
2151
+ // Upstash's max query limit
2152
+ filter: filterString,
2153
+ includeVectors: needsVectors,
2154
+ includeMetadata: needsVectors
2155
+ });
2156
+ for (const result of results) {
2157
+ const points = { id: `${result.id}` };
2158
+ if (update.vector) {
2159
+ points.vector = update.vector;
2160
+ } else if (result.vector) {
2161
+ points.vector = result.vector;
2162
+ }
2163
+ if (update.metadata) {
2164
+ points.metadata = update.metadata;
2165
+ } else if (result.metadata) {
2166
+ points.metadata = result.metadata;
2167
+ }
2168
+ if (sparseVector) points.sparseVector = sparseVector;
2169
+ await ns.upsert(points);
2170
+ }
2171
+ }
2172
+ }
2050
2173
  } catch (error$1) {
2174
+ if (error$1 instanceof error.MastraError) throw error$1;
2051
2175
  throw new error.MastraError(
2052
2176
  {
2053
2177
  id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2054
2178
  domain: error.ErrorDomain.STORAGE,
2055
2179
  category: error.ErrorCategory.THIRD_PARTY,
2056
- details: { namespace, id }
2180
+ details: {
2181
+ namespace,
2182
+ ..."id" in params && params.id && { id: params.id },
2183
+ ..."filter" in params && params.filter && { filter: JSON.stringify(params.filter) }
2184
+ }
2057
2185
  },
2058
2186
  error$1
2059
2187
  );
@@ -2068,22 +2196,109 @@ var UpstashVector = class extends vector.MastraVector {
2068
2196
  */
2069
2197
  async deleteVector({ indexName: namespace, id }) {
2070
2198
  try {
2071
- await this.client.delete(id, {
2072
- namespace
2073
- });
2199
+ const ns = this.client.namespace(namespace);
2200
+ await ns.delete(id);
2074
2201
  } catch (error$1) {
2075
2202
  const mastraError = new error.MastraError(
2076
2203
  {
2077
2204
  id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTOR_FAILED",
2078
2205
  domain: error.ErrorDomain.STORAGE,
2079
2206
  category: error.ErrorCategory.THIRD_PARTY,
2080
- details: { namespace, id }
2207
+ details: {
2208
+ namespace,
2209
+ ...id && { id }
2210
+ }
2081
2211
  },
2082
2212
  error$1
2083
2213
  );
2084
2214
  this.logger?.error(mastraError.toString());
2085
2215
  }
2086
2216
  }
2217
+ /**
2218
+ * Deletes multiple vectors by IDs or filter.
2219
+ * @param indexName - The name of the namespace containing the vectors.
2220
+ * @param ids - Array of vector IDs to delete (mutually exclusive with filter).
2221
+ * @param filter - Filter to match vectors to delete (mutually exclusive with ids).
2222
+ * @returns A promise that resolves when the deletion is complete.
2223
+ * @throws Will throw an error if both ids and filter are provided, or if neither is provided.
2224
+ */
2225
+ async deleteVectors({ indexName: namespace, filter, ids }) {
2226
+ if (ids && filter) {
2227
+ throw new error.MastraError({
2228
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_MUTUALLY_EXCLUSIVE",
2229
+ text: "Cannot specify both ids and filter - they are mutually exclusive",
2230
+ domain: error.ErrorDomain.STORAGE,
2231
+ category: error.ErrorCategory.USER,
2232
+ details: { namespace }
2233
+ });
2234
+ }
2235
+ if (!ids && !filter) {
2236
+ throw new error.MastraError({
2237
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_NO_TARGET",
2238
+ text: "Either filter or ids must be provided",
2239
+ domain: error.ErrorDomain.STORAGE,
2240
+ category: error.ErrorCategory.USER,
2241
+ details: { namespace }
2242
+ });
2243
+ }
2244
+ if (ids && ids.length === 0) {
2245
+ throw new error.MastraError({
2246
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_EMPTY_IDS",
2247
+ text: "Cannot delete with empty ids array",
2248
+ domain: error.ErrorDomain.STORAGE,
2249
+ category: error.ErrorCategory.USER,
2250
+ details: { namespace }
2251
+ });
2252
+ }
2253
+ if (filter && Object.keys(filter).length === 0) {
2254
+ throw new error.MastraError({
2255
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_EMPTY_FILTER",
2256
+ text: "Cannot delete with empty filter object",
2257
+ domain: error.ErrorDomain.STORAGE,
2258
+ category: error.ErrorCategory.USER,
2259
+ details: { namespace }
2260
+ });
2261
+ }
2262
+ try {
2263
+ const ns = this.client.namespace(namespace);
2264
+ if (ids) {
2265
+ await ns.delete(ids);
2266
+ } else if (filter) {
2267
+ const filterString = this.transformFilter(filter);
2268
+ if (filterString) {
2269
+ const stats = await this.describeIndex({ indexName: namespace });
2270
+ const dummyVector = new Array(stats.dimension).fill(1 / Math.sqrt(stats.dimension));
2271
+ const results = await ns.query({
2272
+ vector: dummyVector,
2273
+ topK: 1e3,
2274
+ // Upstash's max query limit
2275
+ filter: filterString,
2276
+ includeVectors: false,
2277
+ includeMetadata: false
2278
+ });
2279
+ const idsToDelete = results.map((r) => `${r.id}`);
2280
+ if (idsToDelete.length > 0) {
2281
+ await ns.delete(idsToDelete);
2282
+ }
2283
+ }
2284
+ }
2285
+ } catch (error$1) {
2286
+ if (error$1 instanceof error.MastraError) throw error$1;
2287
+ throw new error.MastraError(
2288
+ {
2289
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_FAILED",
2290
+ domain: error.ErrorDomain.STORAGE,
2291
+ category: error.ErrorCategory.THIRD_PARTY,
2292
+ details: {
2293
+ namespace,
2294
+ ...filter && { filter: JSON.stringify(filter) },
2295
+ ...ids && { idsCount: ids.length }
2296
+ }
2297
+ },
2298
+ error$1
2299
+ );
2300
+ }
2301
+ }
2087
2302
  };
2088
2303
 
2089
2304
  // src/vector/prompt.ts