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