@mastra/mongodb 1.5.4 → 1.5.5-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,14 @@
1
1
  # @mastra/mongodb
2
2
 
3
+ ## 1.5.5-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed slow semantic recall on large threads in the MongoDB and DynamoDB memory stores. Included message lookups now avoid unnecessary work when semantic recall only needs specific messages and nearby context. (Fixes #11702) ([#14022](https://github.com/mastra-ai/mastra/pull/14022))
8
+
9
+ - 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)]:
10
+ - @mastra/core@1.11.0-alpha.0
11
+
3
12
  ## 1.5.4
4
13
 
5
14
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-mongodb
3
3
  description: Documentation for @mastra/mongodb. Use when working with @mastra/mongodb APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/mongodb"
6
- version: "1.5.4"
6
+ version: "1.5.5-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.5.4",
2
+ "version": "1.5.5-alpha.0",
3
3
  "package": "@mastra/mongodb",
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
 
@@ -136,7 +136,7 @@ const memoryStore = await storage.getStore('memory')
136
136
  const thread = await memoryStore?.getThreadById({ threadId: '...' })
137
137
  ```
138
138
 
139
- > **Warning:** If `init()` is not called, collections won't be created and storage operations will fail silently or throw errors.
139
+ > **Warning:** If `init()` isn't called, collections won't be created and storage operations will fail silently or throw errors.
140
140
 
141
141
  ## Vector Search Capabilities
142
142
 
package/dist/index.cjs CHANGED
@@ -14,7 +14,7 @@ var evals = require('@mastra/core/evals');
14
14
 
15
15
  // package.json
16
16
  var package_default = {
17
- version: "1.5.4"};
17
+ version: "1.5.5-alpha.0"};
18
18
  var MongoDBFilterTranslator = class extends filter.BaseFilterTranslator {
19
19
  getSupportedOperators() {
20
20
  return {
@@ -2907,22 +2907,37 @@ var MemoryStorageMongoDB = class _MemoryStorageMongoDB extends storage.MemorySto
2907
2907
  if (row.type && row.type !== "v2") result.type = row.type;
2908
2908
  return result;
2909
2909
  }
2910
+ _sortMessages(messages, field, direction) {
2911
+ return messages.sort((a, b) => {
2912
+ const isDateField = field === "createdAt" || field === "updatedAt";
2913
+ const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
2914
+ const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
2915
+ if (typeof aValue === "number" && typeof bValue === "number") {
2916
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
2917
+ }
2918
+ return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
2919
+ });
2920
+ }
2910
2921
  async _getIncludedMessages({ include }) {
2911
2922
  if (!include || include.length === 0) return null;
2912
2923
  const collection = await this.getCollection(storage.TABLE_MESSAGES);
2924
+ const targetIds = include.map((inc) => inc.id).filter(Boolean);
2925
+ if (targetIds.length === 0) return null;
2926
+ const targetDocs = await collection.find({ id: { $in: targetIds } }, { projection: { id: 1, thread_id: 1, createdAt: 1 } }).toArray();
2927
+ if (targetDocs.length === 0) return null;
2928
+ const targetMap = new Map(
2929
+ targetDocs.map((doc) => [doc.id, { threadId: doc.thread_id, createdAt: doc.createdAt }])
2930
+ );
2913
2931
  const includedMessages = [];
2914
2932
  for (const inc of include) {
2915
2933
  const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
2916
- const targetMessage = await collection.findOne({ id });
2917
- if (!targetMessage) continue;
2918
- const messageThreadId = targetMessage.thread_id;
2919
- const allMessages = await collection.find({ thread_id: messageThreadId }).sort({ createdAt: 1 }).toArray();
2920
- const targetIndex = allMessages.findIndex((msg) => msg.id === id);
2921
- if (targetIndex === -1) continue;
2922
- const startIndex = Math.max(0, targetIndex - withPreviousMessages);
2923
- const endIndex = Math.min(allMessages.length - 1, targetIndex + withNextMessages);
2924
- for (let i = startIndex; i <= endIndex; i++) {
2925
- includedMessages.push(allMessages[i]);
2934
+ const target = targetMap.get(id);
2935
+ if (!target) continue;
2936
+ const prevMessages = await collection.find({ thread_id: target.threadId, createdAt: { $lte: target.createdAt } }).sort({ createdAt: -1, id: -1 }).limit(withPreviousMessages + 1).toArray();
2937
+ includedMessages.push(...prevMessages);
2938
+ if (withNextMessages > 0) {
2939
+ const nextMessages = await collection.find({ thread_id: target.threadId, createdAt: { $gt: target.createdAt } }).sort({ createdAt: 1, id: 1 }).limit(withNextMessages).toArray();
2940
+ includedMessages.push(...nextMessages);
2926
2941
  }
2927
2942
  }
2928
2943
  const seen = /* @__PURE__ */ new Set();
@@ -2998,6 +3013,20 @@ var MemoryStorageMongoDB = class _MemoryStorageMongoDB extends storage.MemorySto
2998
3013
  const endOp = filter.dateRange.endExclusive ? "$lt" : "$lte";
2999
3014
  query.createdAt = { ...query.createdAt, [endOp]: formatDateForMongoDB(filter.dateRange.end) };
3000
3015
  }
3016
+ if (perPage === 0 && (!include || include.length === 0)) {
3017
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
3018
+ }
3019
+ if (perPage === 0 && include && include.length > 0) {
3020
+ const includeMessages = await this._getIncludedMessages({ include });
3021
+ const list2 = new agent.MessageList().add(includeMessages ?? [], "memory");
3022
+ return {
3023
+ messages: this._sortMessages(list2.get.all.db(), field, direction),
3024
+ total: 0,
3025
+ page,
3026
+ perPage: perPageForResponse,
3027
+ hasMore: false
3028
+ };
3029
+ }
3001
3030
  const total = await collection.countDocuments(query);
3002
3031
  const messages = [];
3003
3032
  if (perPage !== 0) {
@@ -3031,16 +3060,7 @@ var MemoryStorageMongoDB = class _MemoryStorageMongoDB extends storage.MemorySto
3031
3060
  }
3032
3061
  }
3033
3062
  const list = new agent.MessageList().add(messages, "memory");
3034
- let finalMessages = list.get.all.db();
3035
- finalMessages = finalMessages.sort((a, b) => {
3036
- const isDateField = field === "createdAt" || field === "updatedAt";
3037
- const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
3038
- const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
3039
- if (typeof aValue === "number" && typeof bValue === "number") {
3040
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
3041
- }
3042
- return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
3043
- });
3063
+ const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
3044
3064
  const threadIdSet = new Set(threadIds);
3045
3065
  const returnedThreadMessageIds = new Set(
3046
3066
  finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
@@ -3118,6 +3138,23 @@ var MemoryStorageMongoDB = class _MemoryStorageMongoDB extends storage.MemorySto
3118
3138
  const endOp = filter.dateRange.endExclusive ? "$lt" : "$lte";
3119
3139
  query.createdAt = { ...query.createdAt, [endOp]: formatDateForMongoDB(filter.dateRange.end) };
3120
3140
  }
3141
+ if (perPage === 0 && (!include || include.length === 0)) {
3142
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
3143
+ }
3144
+ if (perPage === 0 && include && include.length > 0) {
3145
+ const includeMessages = await this._getIncludedMessages({ include });
3146
+ if (!includeMessages || includeMessages.length === 0) {
3147
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
3148
+ }
3149
+ const list2 = new agent.MessageList().add(includeMessages, "memory");
3150
+ return {
3151
+ messages: this._sortMessages(list2.get.all.db(), field, direction),
3152
+ total: 0,
3153
+ page,
3154
+ perPage: perPageForResponse,
3155
+ hasMore: false
3156
+ };
3157
+ }
3121
3158
  const total = await collection.countDocuments(query);
3122
3159
  const messages = [];
3123
3160
  if (perPage !== 0) {
@@ -3151,16 +3188,7 @@ var MemoryStorageMongoDB = class _MemoryStorageMongoDB extends storage.MemorySto
3151
3188
  }
3152
3189
  }
3153
3190
  const list = new agent.MessageList().add(messages, "memory");
3154
- let finalMessages = list.get.all.db();
3155
- finalMessages = finalMessages.sort((a, b) => {
3156
- const isDateField = field === "createdAt" || field === "updatedAt";
3157
- const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
3158
- const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
3159
- if (typeof aValue === "number" && typeof bValue === "number") {
3160
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
3161
- }
3162
- return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
3163
- });
3191
+ const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
3164
3192
  const hasMore = perPageInput !== false && offset + perPage < total;
3165
3193
  return {
3166
3194
  messages: finalMessages,
@@ -4995,7 +5023,8 @@ Note: This migration may take some time for large collections.
4995
5023
  }
4996
5024
  async listTraces(args) {
4997
5025
  const { filters, pagination, orderBy } = storage.listTracesArgsSchema.parse(args);
4998
- const { page, perPage } = pagination;
5026
+ const page = pagination?.page ?? 0;
5027
+ const perPage = pagination?.perPage ?? 10;
4999
5028
  try {
5000
5029
  const collection = await this.getCollection(storage.TABLE_SPANS);
5001
5030
  const mongoFilter = {
@@ -5100,8 +5129,8 @@ Note: This migration may take some time for large collections.
5100
5129
  if (andConditions.length) {
5101
5130
  mongoFilter.$and = andConditions;
5102
5131
  }
5103
- const sortField = orderBy.field;
5104
- const sortDirection = orderBy.direction === "ASC" ? 1 : -1;
5132
+ const sortField = orderBy?.field ?? "startedAt";
5133
+ const sortDirection = (orderBy?.direction ?? "DESC") === "ASC" ? 1 : -1;
5105
5134
  if (filters?.hasChildError !== void 0) {
5106
5135
  const pipeline = [
5107
5136
  { $match: mongoFilter },