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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @mastra/upstash
2
2
 
3
+ ## 1.0.0-beta.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Add new deleteVectors, updateVector by filter ([#10408](https://github.com/mastra-ai/mastra/pull/10408))
8
+
9
+ - Fix message sorting in listMessages when using semantic recall (include parameter). Messages are now always sorted by createdAt instead of storage order, ensuring correct chronological ordering of conversation history. ([#10491](https://github.com/mastra-ai/mastra/pull/10491))
10
+
11
+ - Updated dependencies [[`21a15de`](https://github.com/mastra-ai/mastra/commit/21a15de369fe82aac26bb642ed7be73505475e8b), [`feb7ee4`](https://github.com/mastra-ai/mastra/commit/feb7ee4d09a75edb46c6669a3beaceec78811747), [`b0e2ea5`](https://github.com/mastra-ai/mastra/commit/b0e2ea5b52c40fae438b9e2f7baee6f0f89c5442), [`c456e01`](https://github.com/mastra-ai/mastra/commit/c456e0149e3c176afcefdbd9bb1d2c5917723725), [`ab035c2`](https://github.com/mastra-ai/mastra/commit/ab035c2ef6d8cc7bb25f06f1a38508bd9e6f126b), [`1a46a56`](https://github.com/mastra-ai/mastra/commit/1a46a566f45a3fcbadc1cf36bf86d351f264bfa3), [`3cf540b`](https://github.com/mastra-ai/mastra/commit/3cf540b9fbfea8f4fc8d3a2319a4e6c0b0cbfd52), [`1c6ce51`](https://github.com/mastra-ai/mastra/commit/1c6ce51f875915ab57fd36873623013699a2a65d), [`898a972`](https://github.com/mastra-ai/mastra/commit/898a9727d286c2510d6b702dfd367e6aaf5c6b0f), [`a97003a`](https://github.com/mastra-ai/mastra/commit/a97003aa1cf2f4022a41912324a1e77263b326b8), [`ccc141e`](https://github.com/mastra-ai/mastra/commit/ccc141ed27da0abc3a3fc28e9e5128152e8e37f4), [`fe3b897`](https://github.com/mastra-ai/mastra/commit/fe3b897c2ccbcd2b10e81b099438c7337feddf89), [`00123ba`](https://github.com/mastra-ai/mastra/commit/00123ba96dc9e5cd0b110420ebdba56d8f237b25), [`29c4309`](https://github.com/mastra-ai/mastra/commit/29c4309f818b24304c041bcb4a8f19b5f13f6b62), [`16785ce`](https://github.com/mastra-ai/mastra/commit/16785ced928f6f22638f4488cf8a125d99211799), [`de8239b`](https://github.com/mastra-ai/mastra/commit/de8239bdcb1d8c0cfa06da21f1569912a66bbc8a), [`b5e6cd7`](https://github.com/mastra-ai/mastra/commit/b5e6cd77fc8c8e64e0494c1d06cee3d84e795d1e), [`3759cb0`](https://github.com/mastra-ai/mastra/commit/3759cb064935b5f74c65ac2f52a1145f7352899d), [`651e772`](https://github.com/mastra-ai/mastra/commit/651e772eb1475fb13e126d3fcc01751297a88214), [`b61b93f`](https://github.com/mastra-ai/mastra/commit/b61b93f9e058b11dd2eec169853175d31dbdd567), [`bae33d9`](https://github.com/mastra-ai/mastra/commit/bae33d91a63fbb64d1e80519e1fc1acaed1e9013), [`c0b731f`](https://github.com/mastra-ai/mastra/commit/c0b731fb27d712dc8582e846df5c0332a6a0c5ba), [`43ca8f2`](https://github.com/mastra-ai/mastra/commit/43ca8f2c7334851cc7b4d3d2f037d8784bfbdd5f), [`2ca67cc`](https://github.com/mastra-ai/mastra/commit/2ca67cc3bb1f6a617353fdcab197d9efebe60d6f), [`9e67002`](https://github.com/mastra-ai/mastra/commit/9e67002b52c9be19936c420a489dbee9c5fd6a78), [`35edc49`](https://github.com/mastra-ai/mastra/commit/35edc49ac0556db609189641d6341e76771b81fc)]:
12
+ - @mastra/core@1.0.0-beta.5
13
+
3
14
  ## 1.0.0-beta.1
4
15
 
5
16
  ### Patch Changes
package/README.md CHANGED
@@ -161,6 +161,18 @@ The Upstash store requires the following configuration:
161
161
  - `UPSTASH_REDIS_REST_URL`: Your Upstash Redis REST URL
162
162
  - `UPSTASH_REDIS_REST_TOKEN`: Your Upstash Redis REST token
163
163
 
164
+ ## Vector Store Methods
165
+
166
+ - `createIndex({ indexName, dimension, metric? })`: Create a new namespace
167
+ - `upsert({ indexName, vectors, sparseVectors?, metadata?, ids? })`: Add or update vectors (supports hybrid search)
168
+ - `query({ indexName, queryVector, sparseQueryVector?, topK?, filter?, includeVector?, includeMetadata?, mode?, fusionAlgorithm? })`: Search for similar vectors
169
+ - `updateVector({ indexName, id?, filter?, update })`: Update a single vector by ID or metadata filter
170
+ - `deleteVector({ indexName, id })`: Delete a single vector by ID
171
+ - `deleteVectors({ indexName, ids?, filter? })`: Delete multiple vectors by IDs or metadata filter
172
+ - `listIndexes()`: List all namespaces
173
+ - `describeIndex({ indexName })`: Get namespace statistics
174
+ - `deleteIndex({ indexName })`: Delete a namespace
175
+
164
176
  ## Features
165
177
 
166
178
  - Serverless vector database and key-value store
package/dist/index.cjs CHANGED
@@ -500,13 +500,11 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
500
500
  }
501
501
  return 0;
502
502
  };
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
- }
503
+ messagesData.sort((a, b) => {
504
+ const aValue = getFieldValue(a);
505
+ const bValue = getFieldValue(b);
506
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
507
+ });
510
508
  const total = messagesData.length;
511
509
  const start = offset;
512
510
  const end = perPageInput === false ? total : start + perPage;
@@ -527,23 +525,11 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
527
525
  }
528
526
  const list = new agent.MessageList().add(allMessages, "memory");
529
527
  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
- }
528
+ finalMessages = finalMessages.sort((a, b) => {
529
+ const aValue = getFieldValue(a);
530
+ const bValue = getFieldValue(b);
531
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
532
+ });
547
533
  const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
548
534
  const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
549
535
  const hasMore = perPageInput !== false && !allThreadMessagesReturned && end < total;
@@ -1011,7 +997,9 @@ var ScoresUpstash = class extends storage.ScoresStorage {
1011
997
  id: "STORAGE_UPSTASH_STORAGE_GET_SCORE_BY_ID_FAILED",
1012
998
  domain: error.ErrorDomain.STORAGE,
1013
999
  category: error.ErrorCategory.THIRD_PARTY,
1014
- details: { id }
1000
+ details: {
1001
+ ...id && { id }
1002
+ }
1015
1003
  },
1016
1004
  error$1
1017
1005
  );
@@ -2001,6 +1989,11 @@ var UpstashVector = class extends vector.MastraVector {
2001
1989
  try {
2002
1990
  await this.client.deleteNamespace(namespace);
2003
1991
  } catch (error$1) {
1992
+ const errorMessage = error$1?.message || "";
1993
+ if (errorMessage.includes("does not exist") || errorMessage.includes("not found")) {
1994
+ this.logger.info(`Namespace ${namespace} does not exist, treating as already deleted`);
1995
+ return;
1996
+ }
2004
1997
  throw new error.MastraError(
2005
1998
  {
2006
1999
  id: "STORAGE_UPSTASH_VECTOR_DELETE_INDEX_FAILED",
@@ -2013,47 +2006,124 @@ var UpstashVector = class extends vector.MastraVector {
2013
2006
  }
2014
2007
  }
2015
2008
  /**
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.
2009
+ * Updates a vector by its ID or multiple vectors matching a filter.
2010
+ * @param params - Parameters containing the id or filter for targeting the vector(s) to update
2011
+ * @param params.indexName - The name of the namespace containing the vector.
2012
+ * @param params.id - The ID of the vector to update (mutually exclusive with filter).
2013
+ * @param params.filter - Filter to match multiple vectors to update (mutually exclusive with id).
2014
+ * @param params.update - An object containing the vector and/or metadata to update.
2022
2015
  * @returns A promise that resolves when the update is complete.
2023
2016
  * @throws Will throw an error if no updates are provided or if the update operation fails.
2024
2017
  */
2025
- async updateVector({ indexName: namespace, id, update }) {
2026
- if (!update.vector && !update.metadata && !update.sparseVector) {
2018
+ async updateVector(params) {
2019
+ const { indexName: namespace, update } = params;
2020
+ const upstashUpdate = update;
2021
+ const sparseVector = upstashUpdate.sparseVector;
2022
+ if ("id" in params && params.id && "filter" in params && params.filter) {
2027
2023
  throw new error.MastraError({
2028
- id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2024
+ id: "STORAGE_UPSTASH_VECTOR_UPDATE_MUTUALLY_EXCLUSIVE",
2025
+ text: "Cannot specify both id and filter - they are mutually exclusive",
2029
2026
  domain: error.ErrorDomain.STORAGE,
2030
- category: error.ErrorCategory.THIRD_PARTY,
2031
- details: { namespace, id },
2032
- text: "No update data provided"
2027
+ category: error.ErrorCategory.USER,
2028
+ details: { namespace }
2033
2029
  });
2034
2030
  }
2035
- if (!update.vector && !update.sparseVector && update.metadata) {
2031
+ if (!("id" in params && params.id) && !("filter" in params && params.filter)) {
2036
2032
  throw new error.MastraError({
2037
- id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2033
+ id: "STORAGE_UPSTASH_VECTOR_UPDATE_NO_TARGET",
2034
+ text: "Either id or filter must be provided",
2038
2035
  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"
2036
+ category: error.ErrorCategory.USER,
2037
+ details: { namespace }
2038
+ });
2039
+ }
2040
+ if (!update.vector && !update.metadata && !sparseVector) {
2041
+ throw new error.MastraError({
2042
+ id: "STORAGE_UPSTASH_VECTOR_UPDATE_NO_PAYLOAD",
2043
+ text: "No update data provided",
2044
+ domain: error.ErrorDomain.STORAGE,
2045
+ category: error.ErrorCategory.USER,
2046
+ details: { namespace }
2047
+ });
2048
+ }
2049
+ if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
2050
+ throw new error.MastraError({
2051
+ id: "STORAGE_UPSTASH_VECTOR_UPDATE_EMPTY_FILTER",
2052
+ text: "Filter cannot be an empty filter object",
2053
+ domain: error.ErrorDomain.STORAGE,
2054
+ category: error.ErrorCategory.USER,
2055
+ details: { namespace }
2042
2056
  });
2043
2057
  }
2044
2058
  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 });
2059
+ const ns = this.client.namespace(namespace);
2060
+ if ("id" in params && params.id) {
2061
+ const points = { id: params.id };
2062
+ if (!update.vector || !update.metadata) {
2063
+ try {
2064
+ const existing = await ns.fetch([params.id], {
2065
+ includeVectors: true,
2066
+ includeMetadata: true
2067
+ });
2068
+ if (existing && existing.length > 0 && existing[0]) {
2069
+ if (!update.vector && existing[0]?.vector) {
2070
+ points.vector = existing[0].vector;
2071
+ }
2072
+ if (!update.metadata && existing[0]?.metadata) {
2073
+ points.metadata = existing[0].metadata;
2074
+ }
2075
+ }
2076
+ } catch (fetchError) {
2077
+ this.logger.warn(`Failed to fetch existing vector ${params.id} for partial update: ${fetchError}`);
2078
+ }
2079
+ }
2080
+ if (update.vector) points.vector = update.vector;
2081
+ if (update.metadata) points.metadata = update.metadata;
2082
+ if (sparseVector) points.sparseVector = sparseVector;
2083
+ await ns.upsert(points);
2084
+ } else if ("filter" in params && params.filter) {
2085
+ const filterString = this.transformFilter(params.filter);
2086
+ if (filterString) {
2087
+ const stats = await this.describeIndex({ indexName: namespace });
2088
+ const dummyVector = new Array(stats.dimension).fill(1 / Math.sqrt(stats.dimension));
2089
+ const needsVectors = !update.vector;
2090
+ const results = await ns.query({
2091
+ vector: dummyVector,
2092
+ topK: 1e3,
2093
+ // Upstash's max query limit
2094
+ filter: filterString,
2095
+ includeVectors: needsVectors,
2096
+ includeMetadata: needsVectors
2097
+ });
2098
+ for (const result of results) {
2099
+ const points = { id: `${result.id}` };
2100
+ if (update.vector) {
2101
+ points.vector = update.vector;
2102
+ } else if (result.vector) {
2103
+ points.vector = result.vector;
2104
+ }
2105
+ if (update.metadata) {
2106
+ points.metadata = update.metadata;
2107
+ } else if (result.metadata) {
2108
+ points.metadata = result.metadata;
2109
+ }
2110
+ if (sparseVector) points.sparseVector = sparseVector;
2111
+ await ns.upsert(points);
2112
+ }
2113
+ }
2114
+ }
2050
2115
  } catch (error$1) {
2116
+ if (error$1 instanceof error.MastraError) throw error$1;
2051
2117
  throw new error.MastraError(
2052
2118
  {
2053
2119
  id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
2054
2120
  domain: error.ErrorDomain.STORAGE,
2055
2121
  category: error.ErrorCategory.THIRD_PARTY,
2056
- details: { namespace, id }
2122
+ details: {
2123
+ namespace,
2124
+ ..."id" in params && params.id && { id: params.id },
2125
+ ..."filter" in params && params.filter && { filter: JSON.stringify(params.filter) }
2126
+ }
2057
2127
  },
2058
2128
  error$1
2059
2129
  );
@@ -2068,22 +2138,109 @@ var UpstashVector = class extends vector.MastraVector {
2068
2138
  */
2069
2139
  async deleteVector({ indexName: namespace, id }) {
2070
2140
  try {
2071
- await this.client.delete(id, {
2072
- namespace
2073
- });
2141
+ const ns = this.client.namespace(namespace);
2142
+ await ns.delete(id);
2074
2143
  } catch (error$1) {
2075
2144
  const mastraError = new error.MastraError(
2076
2145
  {
2077
2146
  id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTOR_FAILED",
2078
2147
  domain: error.ErrorDomain.STORAGE,
2079
2148
  category: error.ErrorCategory.THIRD_PARTY,
2080
- details: { namespace, id }
2149
+ details: {
2150
+ namespace,
2151
+ ...id && { id }
2152
+ }
2081
2153
  },
2082
2154
  error$1
2083
2155
  );
2084
2156
  this.logger?.error(mastraError.toString());
2085
2157
  }
2086
2158
  }
2159
+ /**
2160
+ * Deletes multiple vectors by IDs or filter.
2161
+ * @param indexName - The name of the namespace containing the vectors.
2162
+ * @param ids - Array of vector IDs to delete (mutually exclusive with filter).
2163
+ * @param filter - Filter to match vectors to delete (mutually exclusive with ids).
2164
+ * @returns A promise that resolves when the deletion is complete.
2165
+ * @throws Will throw an error if both ids and filter are provided, or if neither is provided.
2166
+ */
2167
+ async deleteVectors({ indexName: namespace, filter, ids }) {
2168
+ if (ids && filter) {
2169
+ throw new error.MastraError({
2170
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_MUTUALLY_EXCLUSIVE",
2171
+ text: "Cannot specify both ids and filter - they are mutually exclusive",
2172
+ domain: error.ErrorDomain.STORAGE,
2173
+ category: error.ErrorCategory.USER,
2174
+ details: { namespace }
2175
+ });
2176
+ }
2177
+ if (!ids && !filter) {
2178
+ throw new error.MastraError({
2179
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_NO_TARGET",
2180
+ text: "Either filter or ids must be provided",
2181
+ domain: error.ErrorDomain.STORAGE,
2182
+ category: error.ErrorCategory.USER,
2183
+ details: { namespace }
2184
+ });
2185
+ }
2186
+ if (ids && ids.length === 0) {
2187
+ throw new error.MastraError({
2188
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_EMPTY_IDS",
2189
+ text: "Cannot delete with empty ids array",
2190
+ domain: error.ErrorDomain.STORAGE,
2191
+ category: error.ErrorCategory.USER,
2192
+ details: { namespace }
2193
+ });
2194
+ }
2195
+ if (filter && Object.keys(filter).length === 0) {
2196
+ throw new error.MastraError({
2197
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_EMPTY_FILTER",
2198
+ text: "Cannot delete with empty filter object",
2199
+ domain: error.ErrorDomain.STORAGE,
2200
+ category: error.ErrorCategory.USER,
2201
+ details: { namespace }
2202
+ });
2203
+ }
2204
+ try {
2205
+ const ns = this.client.namespace(namespace);
2206
+ if (ids) {
2207
+ await ns.delete(ids);
2208
+ } else if (filter) {
2209
+ const filterString = this.transformFilter(filter);
2210
+ if (filterString) {
2211
+ const stats = await this.describeIndex({ indexName: namespace });
2212
+ const dummyVector = new Array(stats.dimension).fill(1 / Math.sqrt(stats.dimension));
2213
+ const results = await ns.query({
2214
+ vector: dummyVector,
2215
+ topK: 1e3,
2216
+ // Upstash's max query limit
2217
+ filter: filterString,
2218
+ includeVectors: false,
2219
+ includeMetadata: false
2220
+ });
2221
+ const idsToDelete = results.map((r) => `${r.id}`);
2222
+ if (idsToDelete.length > 0) {
2223
+ await ns.delete(idsToDelete);
2224
+ }
2225
+ }
2226
+ }
2227
+ } catch (error$1) {
2228
+ if (error$1 instanceof error.MastraError) throw error$1;
2229
+ throw new error.MastraError(
2230
+ {
2231
+ id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTORS_FAILED",
2232
+ domain: error.ErrorDomain.STORAGE,
2233
+ category: error.ErrorCategory.THIRD_PARTY,
2234
+ details: {
2235
+ namespace,
2236
+ ...filter && { filter: JSON.stringify(filter) },
2237
+ ...ids && { idsCount: ids.length }
2238
+ }
2239
+ },
2240
+ error$1
2241
+ );
2242
+ }
2243
+ }
2087
2244
  };
2088
2245
 
2089
2246
  // src/vector/prompt.ts