@mastra/upstash 1.0.3 → 1.0.4-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @mastra/upstash
2
2
 
3
+ ## 1.0.4-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - dependencies updates: ([#13822](https://github.com/mastra-ai/mastra/pull/13822))
8
+ - Updated dependency [`@upstash/redis@^1.36.3` ↗︎](https://www.npmjs.com/package/@upstash/redis/v/1.36.3) (from `^1.36.2`, in `dependencies`)
9
+
10
+ - Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) ([#14022](https://github.com/mastra-ai/mastra/pull/14022))
11
+
12
+ - Updated dependencies [[`4f71b43`](https://github.com/mastra-ai/mastra/commit/4f71b436a4a6b8839842d8da47b57b84509af56c), [`a070277`](https://github.com/mastra-ai/mastra/commit/a07027766ce195ba74d0783116d894cbab25d44c), [`b628b91`](https://github.com/mastra-ai/mastra/commit/b628b9128b372c0f54214d902b07279f03443900), [`332c014`](https://github.com/mastra-ai/mastra/commit/332c014e076b81edf7fe45b58205882726415e90), [`6b63153`](https://github.com/mastra-ai/mastra/commit/6b63153878ea841c0f4ce632ba66bb33e57e9c1b), [`4246e34`](https://github.com/mastra-ai/mastra/commit/4246e34cec9c26636d0965942268e6d07c346671), [`b8837ee`](https://github.com/mastra-ai/mastra/commit/b8837ee77e2e84197609762bfabd8b3da326d30c), [`5d950f7`](https://github.com/mastra-ai/mastra/commit/5d950f7bf426a215a1808f0abef7de5c8336ba1c), [`28c85b1`](https://github.com/mastra-ai/mastra/commit/28c85b184fc32b40f7f160483c982da6d388ecbd), [`e9a08fb`](https://github.com/mastra-ai/mastra/commit/e9a08fbef1ada7e50e961e2f54f55e8c10b4a45c), [`631ffd8`](https://github.com/mastra-ai/mastra/commit/631ffd82fed108648b448b28e6a90e38c5f53bf5), [`aae2295`](https://github.com/mastra-ai/mastra/commit/aae2295838a2d329ad6640829e87934790ffe5b8), [`aa61f29`](https://github.com/mastra-ai/mastra/commit/aa61f29ff8095ce46a4ae16e46c4d8c79b2b685b), [`7ff3714`](https://github.com/mastra-ai/mastra/commit/7ff37148515439bb3be009a60e02c3e363299760), [`41d79a1`](https://github.com/mastra-ai/mastra/commit/41d79a14bd8cb6de1e2565fd0a04786bae2f211b), [`e673376`](https://github.com/mastra-ai/mastra/commit/e6733763ad1321aa7e5ae15096b9c2104f93b1f3), [`b2204c9`](https://github.com/mastra-ai/mastra/commit/b2204c98a42848bbfb6f0440f005dc2b6354f1cd), [`a1bf1e3`](https://github.com/mastra-ai/mastra/commit/a1bf1e385ed4c0ef6f11b56c5887442970d127f2), [`b6f647a`](https://github.com/mastra-ai/mastra/commit/b6f647ae2388e091f366581595feb957e37d5b40), [`0c57b8b`](https://github.com/mastra-ai/mastra/commit/0c57b8b0a69a97b5a4ae3f79be6c610f29f3cf7b), [`b081f27`](https://github.com/mastra-ai/mastra/commit/b081f272cf411716e1d6bd72ceac4bcee2657b19), [`0c09eac`](https://github.com/mastra-ai/mastra/commit/0c09eacb1926f64cfdc9ae5c6d63385cf8c9f72c), [`6b9b93d`](https://github.com/mastra-ai/mastra/commit/6b9b93d6f459d1ba6e36f163abf62a085ddb3d64), [`31b6067`](https://github.com/mastra-ai/mastra/commit/31b6067d0cc3ab10e1b29c36147f3b5266bc714a), [`797ac42`](https://github.com/mastra-ai/mastra/commit/797ac4276de231ad2d694d9aeca75980f6cd0419), [`0bc289e`](https://github.com/mastra-ai/mastra/commit/0bc289e2d476bf46c5b91c21969e8d0c6864691c), [`9b75a06`](https://github.com/mastra-ai/mastra/commit/9b75a06e53ebb0b950ba7c1e83a0142047185f46), [`4c3a1b1`](https://github.com/mastra-ai/mastra/commit/4c3a1b122ea083e003d71092f30f3b31680b01c0), [`85cc3b3`](https://github.com/mastra-ai/mastra/commit/85cc3b3b6f32ae4b083c26498f50d5b250ba944b), [`97ea28c`](https://github.com/mastra-ai/mastra/commit/97ea28c746e9e4147d56047bbb1c4a92417a3fec), [`d567299`](https://github.com/mastra-ai/mastra/commit/d567299cf81e02bd9d5221d4bc05967d6c224161), [`716ffe6`](https://github.com/mastra-ai/mastra/commit/716ffe68bed81f7c2690bc8581b9e140f7bf1c3d), [`8296332`](https://github.com/mastra-ai/mastra/commit/8296332de21c16e3dfc3d0b2d615720a6dc88f2f), [`4df2116`](https://github.com/mastra-ai/mastra/commit/4df211619dd922c047d396ca41cd7027c8c4c8e7), [`2219c1a`](https://github.com/mastra-ai/mastra/commit/2219c1acbd21da116da877f0036ffb985a9dd5a3), [`17c4145`](https://github.com/mastra-ai/mastra/commit/17c4145166099354545582335b5252bdfdfd908b)]:
13
+ - @mastra/core@1.11.0-alpha.0
14
+
3
15
  ## 1.0.3
4
16
 
5
17
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-upstash
3
3
  description: Documentation for @mastra/upstash. Use when working with @mastra/upstash APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/upstash"
6
- version: "1.0.3"
6
+ version: "1.0.4-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.3",
2
+ "version": "1.0.4-alpha.0",
3
3
  "package": "@mastra/upstash",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -170,11 +170,11 @@ const memory = new Memory({
170
170
 
171
171
  ## Designing Effective Templates
172
172
 
173
- A well-structured template keeps the information easy for the agent to parse and update. Treat the template as a short form that you want the assistant to keep up to date.
173
+ A well-structured template keeps the information straightforward for the agent to parse and update. Treat the template as a short form that you want the assistant to keep up to date.
174
174
 
175
- - **Short, focused labels.** Avoid paragraphs or very long headings. Keep labels brief (for example `## Personal Info` or `- Name:`) so updates are easy to read and less likely to be truncated.
175
+ - **Short, focused labels.** Avoid paragraphs or very long headings. Keep labels brief (for example `## Personal Info` or `- Name:`) so updates are readable and less likely to be truncated.
176
176
  - **Use consistent casing.** Inconsistent capitalization (`Timezone:` vs `timezone:`) can cause messy updates. Stick to Title Case or lower case for headings and bullet labels.
177
- - **Keep placeholder text simple.** Use hints such as `[e.g., Formal]` or `[Date]` to help the LLM fill in the correct spots.
177
+ - **Keep placeholder text minimal.** Use hints such as `[e.g., Formal]` or `[Date]` to help the LLM fill in the correct spots.
178
178
  - **Abbreviate very long values.** If you only need a short form, include guidance like `- Name: [First name or nickname]` or `- Address (short):` rather than the full legal text.
179
179
  - **Mention update rules in `instructions`.** You can instruct how and when to fill or clear parts of the template directly in the agent's `instructions` field.
180
180
 
@@ -263,13 +263,13 @@ Schema-based working memory uses **merge semantics**, meaning the agent only nee
263
263
 
264
264
  - **Object fields are deep merged:** Only provided fields are updated; others remain unchanged
265
265
  - **Set a field to `null` to delete it:** This explicitly removes the field from memory
266
- - **Arrays are replaced entirely:** When an array field is provided, it replaces the existing array (arrays are not merged element-by-element)
266
+ - **Arrays are replaced entirely:** When an array field is provided, it replaces the existing array (arrays aren't merged element-by-element)
267
267
 
268
268
  ## Choosing Between Template and Schema
269
269
 
270
270
  - Use a **template** (Markdown) if you want the agent to maintain memory as a free-form text block, such as a user profile or scratchpad. Templates use **replace semantics** — the agent must provide the complete memory content on each update.
271
271
  - Use a **schema** if you need structured, type-safe data that can be validated and programmatically accessed as JSON. Schemas use **merge semantics** — the agent only provides fields to update, and existing fields are preserved.
272
- - Only one mode can be active at a time: setting both `template` and `schema` is not supported.
272
+ - Only one mode can be active at a time: setting both `template` and `schema` isn't supported.
273
273
 
274
274
  ## Example: Multi-step Retention
275
275
 
@@ -301,7 +301,7 @@ Below is a simplified view of how the `User Profile` template updates across a s
301
301
 
302
302
  The agent can now refer to `Sam` or `Berlin` in later responses without requesting the information again because it has been stored in working memory.
303
303
 
304
- If your agent is not properly updating working memory when you expect it to, you can add system instructions on _how_ and _when_ to use this template in your agent's `instructions` setting.
304
+ If your agent isn't properly updating working memory when you expect it to, you can add system instructions on _how_ and _when_ to use this template in your agent's `instructions` setting.
305
305
 
306
306
  ## Setting Initial Working Memory
307
307
 
@@ -359,7 +359,7 @@ The dimension size must match the output dimension of your chosen embedding mode
359
359
  - Cohere embed-multilingual-v3: 1024 dimensions
360
360
  - Google gemini-embedding-001: 768 dimensions (or custom)
361
361
 
362
- > **Warning:** Index dimensions cannot be changed after creation. To use a different model, delete and recreate the index with the new dimension size.
362
+ > **Warning:** Index dimensions can't be changed after creation. To use a different model, delete and recreate the index with the new dimension size.
363
363
 
364
364
  ### Naming Rules for Databases
365
365
 
@@ -583,7 +583,7 @@ Key metadata considerations:
583
583
 
584
584
  ## Deleting Vectors
585
585
 
586
- When building RAG applications, you often need to clean up stale vectors when documents are deleted or updated. Mastra provides the `deleteVectors` method that supports deleting vectors by metadata filters, making it easy to remove all embeddings associated with a specific document.
586
+ When building RAG applications, you often need to clean up stale vectors when documents are deleted or updated. Mastra provides the `deleteVectors` method that supports deleting vectors by metadata filters, making it straightforward to remove all embeddings associated with a specific document.
587
587
 
588
588
  ### Delete by Metadata Filter
589
589
 
@@ -4,7 +4,7 @@ The Upstash storage implementation provides a serverless-friendly storage soluti
4
4
 
5
5
  > **Pricing:** When using Mastra with Upstash, the pay-as-you-go model can result in unexpectedly high costs due to the high volume of Redis commands generated during agent conversations. We strongly recommend using a **fixed pricing plan** for predictable costs. See [Upstash pricing](https://upstash.com/pricing/redis) for details and [GitHub issue #5850](https://github.com/mastra-ai/mastra/issues/5850) for context.
6
6
 
7
- > **Observability Not Supported:** Upstash storage **does not support the observability domain**. Traces from the `DefaultExporter` cannot be persisted to Upstash, and Mastra Studio's observability features won't work with Upstash as your only storage provider. To enable observability, use [composite storage](https://mastra.ai/reference/storage/composite) to route observability data to a supported provider like ClickHouse or PostgreSQL.
7
+ > **Observability Not Supported:** Upstash storage **doesn't support the observability domain**. Traces from the `DefaultExporter` can't be persisted to Upstash, and Mastra Studio's observability features won't work with Upstash as your only storage provider. To enable observability, use [composite storage](https://mastra.ai/reference/storage/composite) to route observability data to a supported provider like ClickHouse or PostgreSQL.
8
8
 
9
9
  ## Installation
10
10
 
@@ -46,11 +46,11 @@ const storage = new UpstashStore({
46
46
 
47
47
  ## Parameters
48
48
 
49
- **url:** (`string`): Upstash Redis URL
49
+ **url** (`string`): Upstash Redis URL
50
50
 
51
- **token:** (`string`): Upstash Redis authentication token
51
+ **token** (`string`): Upstash Redis authentication token
52
52
 
53
- **prefix?:** (`string`): Key prefix for all stored items (Default: `mastra:`)
53
+ **prefix** (`string`): Key prefix for all stored items (Default: `mastra:`)
54
54
 
55
55
  ## Additional Notes
56
56
 
@@ -4,9 +4,9 @@ The UpstashVector class provides vector search using [Upstash Vector](https://up
4
4
 
5
5
  ## Constructor Options
6
6
 
7
- **url:** (`string`): Upstash Vector database URL
7
+ **url** (`string`): Upstash Vector database URL
8
8
 
9
- **token:** (`string`): Upstash Vector API token
9
+ **token** (`string`): Upstash Vector API token
10
10
 
11
11
  ## Methods
12
12
 
@@ -14,41 +14,41 @@ The UpstashVector class provides vector search using [Upstash Vector](https://up
14
14
 
15
15
  Note: This method is a no-op for Upstash as indexes are created automatically.
16
16
 
17
- **indexName:** (`string`): Name of the index to create
17
+ **indexName** (`string`): Name of the index to create
18
18
 
19
- **dimension:** (`number`): Vector dimension (must match your embedding model)
19
+ **dimension** (`number`): Vector dimension (must match your embedding model)
20
20
 
21
- **metric?:** (`'cosine' | 'euclidean' | 'dotproduct'`): Distance metric for similarity search (Default: `cosine`)
21
+ **metric** (`'cosine' | 'euclidean' | 'dotproduct'`): Distance metric for similarity search (Default: `cosine`)
22
22
 
23
23
  ### upsert()
24
24
 
25
- **indexName:** (`string`): Name of the index to upsert into
25
+ **indexName** (`string`): Name of the index to upsert into
26
26
 
27
- **vectors:** (`number[][]`): Array of embedding vectors
27
+ **vectors** (`number[][]`): Array of embedding vectors
28
28
 
29
- **sparseVectors?:** (`{ indices: number[], values: number[] }[]`): Array of sparse vectors for hybrid search. Each sparse vector must have matching indices and values arrays.
29
+ **sparseVectors** (`{ indices: number[], values: number[] }[]`): Array of sparse vectors for hybrid search. Each sparse vector must have matching indices and values arrays.
30
30
 
31
- **metadata?:** (`Record<string, any>[]`): Metadata for each vector
31
+ **metadata** (`Record<string, any>[]`): Metadata for each vector
32
32
 
33
- **ids?:** (`string[]`): Optional vector IDs (auto-generated if not provided)
33
+ **ids** (`string[]`): Optional vector IDs (auto-generated if not provided)
34
34
 
35
35
  ### query()
36
36
 
37
- **indexName:** (`string`): Name of the index to query
37
+ **indexName** (`string`): Name of the index to query
38
38
 
39
- **queryVector:** (`number[]`): Query vector to find similar vectors
39
+ **queryVector** (`number[]`): Query vector to find similar vectors
40
40
 
41
- **sparseVector?:** (`{ indices: number[], values: number[] }`): Optional sparse vector for hybrid search. Must have matching indices and values arrays.
41
+ **sparseVector** (`{ indices: number[], values: number[] }`): Optional sparse vector for hybrid search. Must have matching indices and values arrays.
42
42
 
43
- **topK?:** (`number`): Number of results to return (Default: `10`)
43
+ **topK** (`number`): Number of results to return (Default: `10`)
44
44
 
45
- **filter?:** (`Record<string, any>`): Metadata filters for the query
45
+ **filter** (`Record<string, any>`): Metadata filters for the query
46
46
 
47
- **includeVector?:** (`boolean`): Whether to include vectors in the results (Default: `false`)
47
+ **includeVector** (`boolean`): Whether to include vectors in the results (Default: `false`)
48
48
 
49
- **fusionAlgorithm?:** (`FusionAlgorithm`): Algorithm used to combine dense and sparse search results in hybrid search (e.g., RRF - Reciprocal Rank Fusion)
49
+ **fusionAlgorithm** (`FusionAlgorithm`): Algorithm used to combine dense and sparse search results in hybrid search (e.g., RRF - Reciprocal Rank Fusion)
50
50
 
51
- **queryMode?:** (`QueryMode`): Search mode: 'DENSE' for dense-only, 'SPARSE' for sparse-only, or 'HYBRID' for combined search
51
+ **queryMode** (`QueryMode`): Search mode: 'DENSE' for dense-only, 'SPARSE' for sparse-only, or 'HYBRID' for combined search
52
52
 
53
53
  ### listIndexes()
54
54
 
@@ -56,7 +56,7 @@ Returns an array of index names (namespaces) as strings.
56
56
 
57
57
  ### describeIndex()
58
58
 
59
- **indexName:** (`string`): Name of the index to describe
59
+ **indexName** (`string`): Name of the index to describe
60
60
 
61
61
  Returns:
62
62
 
@@ -70,15 +70,15 @@ interface IndexStats {
70
70
 
71
71
  ### deleteIndex()
72
72
 
73
- **indexName:** (`string`): Name of the index (namespace) to delete
73
+ **indexName** (`string`): Name of the index (namespace) to delete
74
74
 
75
75
  ### updateVector()
76
76
 
77
- **indexName:** (`string`): Name of the index to update
77
+ **indexName** (`string`): Name of the index to update
78
78
 
79
- **id:** (`string`): ID of the item to update
79
+ **id** (`string`): ID of the item to update
80
80
 
81
- **update:** (`object`): Update object containing vector, sparse vector, and/or metadata
81
+ **update** (`object`): Update object containing vector, sparse vector, and/or metadata
82
82
 
83
83
  The `update` object can have the following properties:
84
84
 
@@ -88,9 +88,9 @@ The `update` object can have the following properties:
88
88
 
89
89
  ### deleteVector()
90
90
 
91
- **indexName:** (`string`): Name of the index from which to delete the item
91
+ **indexName** (`string`): Name of the index from which to delete the item
92
92
 
93
- **id:** (`string`): ID of the item to delete
93
+ **id** (`string`): ID of the item to delete
94
94
 
95
95
  Attempts to delete an item by its ID from the specified index. Logs an error message if the deletion fails.
96
96
 
package/dist/index.cjs CHANGED
@@ -504,6 +504,22 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
504
504
  }
505
505
  return messageData.threadId || null;
506
506
  }
507
+ _sortMessages(messages, field, direction) {
508
+ return messages.sort((a, b) => {
509
+ const getVal = (msg) => {
510
+ if (field === "createdAt") {
511
+ return new Date(msg.createdAt).getTime();
512
+ }
513
+ const value = msg[field];
514
+ if (typeof value === "number") return value;
515
+ if (value instanceof Date) return value.getTime();
516
+ return 0;
517
+ };
518
+ const aValue = getVal(a);
519
+ const bValue = getVal(b);
520
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
521
+ });
522
+ }
507
523
  async _getIncludedMessages(include) {
508
524
  if (!include?.length) return [];
509
525
  const messageIds = /* @__PURE__ */ new Set();
@@ -640,11 +656,25 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
640
656
  new Error("page must be >= 0")
641
657
  );
642
658
  }
659
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
660
+ if (perPage === 0 && (!include || include.length === 0)) {
661
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
662
+ }
643
663
  let includedMessages = [];
644
664
  if (include && include.length > 0) {
645
665
  const included = await this._getIncludedMessages(include);
646
666
  includedMessages = included.map(this.parseStoredMessage);
647
667
  }
668
+ if (perPage === 0 && include && include.length > 0) {
669
+ const list2 = new agent.MessageList().add(includedMessages, "memory");
670
+ return {
671
+ messages: this._sortMessages(list2.get.all.db(), field, direction),
672
+ total: 0,
673
+ page,
674
+ perPage: perPageForResponse,
675
+ hasMore: false
676
+ };
677
+ }
648
678
  const allMessageIdsWithThreads = [];
649
679
  for (const tid of threadIds) {
650
680
  const threadMessagesKey = getThreadMessagesKey(tid);
@@ -674,25 +704,7 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
674
704
  (msg) => new Date(msg.createdAt),
675
705
  filter?.dateRange
676
706
  );
677
- const { field, direction } = this.parseOrderBy(orderBy, "ASC");
678
- const getFieldValue = (msg) => {
679
- if (field === "createdAt") {
680
- return new Date(msg.createdAt).getTime();
681
- }
682
- const value = msg[field];
683
- if (typeof value === "number") {
684
- return value;
685
- }
686
- if (value instanceof Date) {
687
- return value.getTime();
688
- }
689
- return 0;
690
- };
691
- messagesData.sort((a, b) => {
692
- const aValue = getFieldValue(a);
693
- const bValue = getFieldValue(b);
694
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
695
- });
707
+ messagesData = this._sortMessages(messagesData, field, direction);
696
708
  const total = messagesData.length;
697
709
  const start = offset;
698
710
  const end = perPageInput === false ? total : start + perPage;
@@ -713,11 +725,7 @@ var StoreMemoryUpstash = class extends storage.MemoryStorage {
713
725
  }
714
726
  const list = new agent.MessageList().add(allMessages, "memory");
715
727
  let finalMessages = list.get.all.db();
716
- finalMessages = finalMessages.sort((a, b) => {
717
- const aValue = getFieldValue(a);
718
- const bValue = getFieldValue(b);
719
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
720
- });
728
+ finalMessages = this._sortMessages(finalMessages, field, direction);
721
729
  const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
722
730
  const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
723
731
  const hasMore = perPageInput !== false && !allThreadMessagesReturned && end < total;