@mastra/convex 1.0.5 → 1.0.6-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/convex
2
2
 
3
+ ## 1.0.6-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 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))
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.0.5
4
13
 
5
14
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-convex
3
3
  description: Documentation for @mastra/convex. Use when working with @mastra/convex APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/convex"
6
- version: "1.0.5"
6
+ version: "1.0.6-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.5",
2
+ "version": "1.0.6-alpha.0",
3
3
  "package": "@mastra/convex",
4
4
  "exports": {
5
5
  "mastraStorage": {
@@ -2,7 +2,7 @@
2
2
 
3
3
  The Convex storage implementation provides a serverless storage solution using [Convex](https://convex.dev), a full-stack TypeScript development platform with real-time sync and automatic caching.
4
4
 
5
- > **Observability Not Supported:** Convex storage **does not support the observability domain**. Traces from the `DefaultExporter` cannot be persisted to Convex, and Mastra Studio's observability features won't work with Convex 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.
5
+ > **Observability Not Supported:** Convex storage **doesn't support the observability domain**. Traces from the `DefaultExporter` can't be persisted to Convex, and Mastra Studio's observability features won't work with Convex 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.
6
6
 
7
7
  > **Record Size Limit:** Convex enforces a **1 MiB maximum record size**. This limit can be exceeded when storing messages with base64-encoded attachments such as images. See [Handling large attachments](https://mastra.ai/docs/memory/storage) for workarounds including uploading attachments to external storage like S3, Cloudflare R2, or [Convex file storage](https://docs.convex.dev/file-storage).
8
8
 
@@ -97,11 +97,11 @@ const storage = new ConvexStore({
97
97
 
98
98
  ## Parameters
99
99
 
100
- **deploymentUrl:** (`string`): Convex deployment URL (e.g., https\://your-project.convex.cloud)
100
+ **deploymentUrl** (`string`): Convex deployment URL (e.g., https\://your-project.convex.cloud)
101
101
 
102
- **adminAuthToken:** (`string`): Convex admin authentication token for backend access
102
+ **adminAuthToken** (`string`): Convex admin authentication token for backend access
103
103
 
104
- **storageFunction?:** (`string`): Path to the storage mutation function (default: 'mastra/storage:handle') (Default: `mastra/storage:handle`)
104
+ **storageFunction** (`string`): Path to the storage mutation function (default: 'mastra/storage:handle') (Default: `mastra/storage:handle`)
105
105
 
106
106
  ## Constructor Examples
107
107
 
@@ -34,11 +34,11 @@ Before using `ConvexVector`, you need to set up the Convex schema and storage ha
34
34
 
35
35
  ## Constructor Options
36
36
 
37
- **deploymentUrl:** (`string`): Convex deployment URL (e.g., https\://your-project.convex.cloud)
37
+ **deploymentUrl** (`string`): Convex deployment URL (e.g., https\://your-project.convex.cloud)
38
38
 
39
- **adminAuthToken:** (`string`): Convex admin authentication token
39
+ **adminAuthToken** (`string`): Convex admin authentication token
40
40
 
41
- **storageFunction?:** (`string`): Path to the storage mutation function (Default: `mastra/storage:handle`)
41
+ **storageFunction** (`string`): Path to the storage mutation function (Default: `mastra/storage:handle`)
42
42
 
43
43
  ## Constructor Examples
44
44
 
@@ -69,11 +69,11 @@ const vectorStore = new ConvexVector({
69
69
 
70
70
  ### createIndex()
71
71
 
72
- **indexName:** (`string`): Name of the index to create
72
+ **indexName** (`string`): Name of the index to create
73
73
 
74
- **dimension:** (`number`): Vector dimension (must match your embedding model)
74
+ **dimension** (`number`): Vector dimension (must match your embedding model)
75
75
 
76
- **metric?:** (`'cosine' | 'euclidean' | 'dotproduct'`): Distance metric for similarity search (only cosine is currently supported) (Default: `cosine`)
76
+ **metric** (`'cosine' | 'euclidean' | 'dotproduct'`): Distance metric for similarity search (only cosine is currently supported) (Default: `cosine`)
77
77
 
78
78
  ```typescript
79
79
  await vectorStore.createIndex({
@@ -84,13 +84,13 @@ await vectorStore.createIndex({
84
84
 
85
85
  ### upsert()
86
86
 
87
- **indexName:** (`string`): Name of the index to upsert vectors into
87
+ **indexName** (`string`): Name of the index to upsert vectors into
88
88
 
89
- **vectors:** (`number[][]`): Array of embedding vectors
89
+ **vectors** (`number[][]`): Array of embedding vectors
90
90
 
91
- **metadata?:** (`Record<string, any>[]`): Metadata for each vector
91
+ **metadata** (`Record<string, any>[]`): Metadata for each vector
92
92
 
93
- **ids?:** (`string[]`): Optional vector IDs (auto-generated if not provided)
93
+ **ids** (`string[]`): Optional vector IDs (auto-generated if not provided)
94
94
 
95
95
  ```typescript
96
96
  await vectorStore.upsert({
@@ -103,15 +103,15 @@ await vectorStore.upsert({
103
103
 
104
104
  ### query()
105
105
 
106
- **indexName:** (`string`): Name of the index to query
106
+ **indexName** (`string`): Name of the index to query
107
107
 
108
- **queryVector:** (`number[]`): Query vector
108
+ **queryVector** (`number[]`): Query vector
109
109
 
110
- **topK?:** (`number`): Number of results to return (Default: `10`)
110
+ **topK** (`number`): Number of results to return (Default: `10`)
111
111
 
112
- **filter?:** (`Record<string, any>`): Metadata filters
112
+ **filter** (`Record<string, any>`): Metadata filters
113
113
 
114
- **includeVector?:** (`boolean`): Whether to include the vector in the result (Default: `false`)
114
+ **includeVector** (`boolean`): Whether to include the vector in the result (Default: `false`)
115
115
 
116
116
  ```typescript
117
117
  const results = await vectorStore.query({
@@ -133,7 +133,7 @@ const indexes = await vectorStore.listIndexes()
133
133
 
134
134
  ### describeIndex()
135
135
 
136
- **indexName:** (`string`): Name of the index to describe
136
+ **indexName** (`string`): Name of the index to describe
137
137
 
138
138
  Returns:
139
139
 
@@ -147,7 +147,7 @@ interface IndexStats {
147
147
 
148
148
  ### deleteIndex()
149
149
 
150
- **indexName:** (`string`): Name of the index to delete
150
+ **indexName** (`string`): Name of the index to delete
151
151
 
152
152
  Deletes the index and all its vectors.
153
153
 
@@ -159,13 +159,13 @@ await vectorStore.deleteIndex({ indexName: 'my_vectors' })
159
159
 
160
160
  Update a single vector by ID or by metadata filter. Either `id` or `filter` must be provided, but not both.
161
161
 
162
- **indexName:** (`string`): Name of the index containing the vector
162
+ **indexName** (`string`): Name of the index containing the vector
163
163
 
164
- **id?:** (`string`): ID of the vector to update (mutually exclusive with filter)
164
+ **id** (`string`): ID of the vector to update (mutually exclusive with filter)
165
165
 
166
- **filter?:** (`Record<string, any>`): Metadata filter to identify vector(s) to update (mutually exclusive with id)
166
+ **filter** (`Record<string, any>`): Metadata filter to identify vector(s) to update (mutually exclusive with id)
167
167
 
168
- **update:** (`{ vector?: number[]; metadata?: Record<string, any>; }`): Object containing the vector and/or metadata to update
168
+ **update** (`{ vector?: number[]; metadata?: Record<string, any>; }`): Object containing the vector and/or metadata to update
169
169
 
170
170
  ```typescript
171
171
  // Update by ID
@@ -190,9 +190,9 @@ await vectorStore.updateVector({
190
190
 
191
191
  ### deleteVector()
192
192
 
193
- **indexName:** (`string`): Name of the index containing the vector
193
+ **indexName** (`string`): Name of the index containing the vector
194
194
 
195
- **id:** (`string`): ID of the vector to delete
195
+ **id** (`string`): ID of the vector to delete
196
196
 
197
197
  ```typescript
198
198
  await vectorStore.deleteVector({ indexName: 'my_vectors', id: 'vector123' })
@@ -202,11 +202,11 @@ await vectorStore.deleteVector({ indexName: 'my_vectors', id: 'vector123' })
202
202
 
203
203
  Delete multiple vectors by IDs or by metadata filter. Either `ids` or `filter` must be provided, but not both.
204
204
 
205
- **indexName:** (`string`): Name of the index containing the vectors to delete
205
+ **indexName** (`string`): Name of the index containing the vectors to delete
206
206
 
207
- **ids?:** (`string[]`): Array of vector IDs to delete (mutually exclusive with filter)
207
+ **ids** (`string[]`): Array of vector IDs to delete (mutually exclusive with filter)
208
208
 
209
- **filter?:** (`Record<string, any>`): Metadata filter to identify vectors to delete (mutually exclusive with ids)
209
+ **filter** (`Record<string, any>`): Metadata filter to identify vectors to delete (mutually exclusive with ids)
210
210
 
211
211
  ```typescript
212
212
  // Delete by IDs
package/dist/index.cjs CHANGED
@@ -324,6 +324,20 @@ var MemoryConvex = class extends storage.MemoryStorage {
324
324
  const perPage = storage.normalizePerPage(perPageInput, 40);
325
325
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
326
326
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
327
+ if (perPage === 0 && (!include || include.length === 0)) {
328
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
329
+ }
330
+ if (perPage === 0 && include && include.length > 0) {
331
+ const messages2 = await this._getIncludedMessages(include);
332
+ const list = new agent.MessageList().add(messages2, "memory");
333
+ return {
334
+ messages: this._sortMessages(list.get.all.db(), field, direction),
335
+ total: 0,
336
+ page,
337
+ perPage: perPageForResponse,
338
+ hasMore: false
339
+ };
340
+ }
327
341
  let rows = [];
328
342
  for (const tid of threadIds) {
329
343
  const threadRows = await this.#db.queryTable(storage.TABLE_MESSAGES, [{ field: "thread_id", value: tid }]);
@@ -343,62 +357,27 @@ var MemoryConvex = class extends storage.MemoryStorage {
343
357
  });
344
358
  const totalThreadMessages = rows.length;
345
359
  const paginatedRows = perPageInput === false ? rows : rows.slice(offset, offset + perPage);
346
- const messages = paginatedRows.map((row) => this.parseStoredMessage(row));
360
+ let messages = paginatedRows.map((row) => this.parseStoredMessage(row));
347
361
  const messageIds = new Set(messages.map((msg) => msg.id));
348
362
  if (include && include.length > 0) {
349
- const threadMessagesCache = /* @__PURE__ */ new Map();
350
- for (const tid of threadIds) {
351
- const tidRows = rows.filter((r) => r.thread_id === tid);
352
- threadMessagesCache.set(tid, tidRows);
353
- }
354
- for (const includeItem of include) {
355
- let targetThreadId;
356
- let target;
357
- for (const [tid, cachedRows] of threadMessagesCache) {
358
- target = cachedRows.find((row) => row.id === includeItem.id);
359
- if (target) {
360
- targetThreadId = tid;
361
- break;
362
- }
363
- }
364
- if (!target) {
365
- const messageRows = await this.#db.queryTable(storage.TABLE_MESSAGES, [
366
- { field: "id", value: includeItem.id }
367
- ]);
368
- if (messageRows.length > 0) {
369
- target = messageRows[0];
370
- targetThreadId = target.thread_id;
371
- if (targetThreadId && !threadMessagesCache.has(targetThreadId)) {
372
- const otherThreadRows = await this.#db.queryTable(storage.TABLE_MESSAGES, [
373
- { field: "thread_id", value: targetThreadId }
374
- ]);
375
- threadMessagesCache.set(targetThreadId, otherThreadRows);
376
- }
377
- }
363
+ const preloadedThreads = /* @__PURE__ */ new Map();
364
+ if (!resourceId && !filter?.dateRange) {
365
+ for (const tid of threadIds) {
366
+ preloadedThreads.set(
367
+ tid,
368
+ rows.filter((r) => r.thread_id === tid)
369
+ );
378
370
  }
379
- if (!target || !targetThreadId) continue;
380
- if (!messageIds.has(target.id)) {
381
- messages.push(this.parseStoredMessage(target));
382
- messageIds.add(target.id);
371
+ }
372
+ const includedMessages = await this._getIncludedMessages(include, preloadedThreads);
373
+ for (const msg of includedMessages) {
374
+ if (!messageIds.has(msg.id)) {
375
+ messages.push(msg);
376
+ messageIds.add(msg.id);
383
377
  }
384
- const targetThreadRows = threadMessagesCache.get(targetThreadId) || [];
385
- await this.addContextMessages({
386
- includeItem,
387
- allMessages: targetThreadRows,
388
- targetThreadId,
389
- messageIds,
390
- messages
391
- });
392
378
  }
393
379
  }
394
- messages.sort((a, b) => {
395
- const aValue = field === "createdAt" || field === "updatedAt" ? new Date(a[field]).getTime() : a[field];
396
- const bValue = field === "createdAt" || field === "updatedAt" ? new Date(b[field]).getTime() : b[field];
397
- if (typeof aValue === "number" && typeof bValue === "number") {
398
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
399
- }
400
- return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
401
- });
380
+ messages = this._sortMessages(messages, field, direction);
402
381
  const hasMore = include && include.length > 0 ? new Set(messages.filter((m) => m.threadId === threadId).map((m) => m.id)).size < totalThreadMessages : perPageInput === false ? false : offset + perPage < totalThreadMessages;
403
382
  return {
404
383
  messages,
@@ -578,6 +557,69 @@ var MemoryConvex = class extends storage.MemoryStorage {
578
557
  await this.saveResource({ resource: updated });
579
558
  return updated;
580
559
  }
560
+ _sortMessages(messages, field, direction) {
561
+ return messages.sort((a, b) => {
562
+ const aValue = field === "createdAt" || field === "updatedAt" ? new Date(a[field]).getTime() : a[field];
563
+ const bValue = field === "createdAt" || field === "updatedAt" ? new Date(b[field]).getTime() : b[field];
564
+ if (typeof aValue === "number" && typeof bValue === "number") {
565
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
566
+ }
567
+ return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
568
+ });
569
+ }
570
+ async _getIncludedMessages(include, preloadedThreads) {
571
+ if (include.length === 0) return [];
572
+ const messages = [];
573
+ const messageIds = /* @__PURE__ */ new Set();
574
+ const threadMessagesCache = new Map(preloadedThreads ?? []);
575
+ const cachedTargets = /* @__PURE__ */ new Map();
576
+ for (const [threadId, rows] of threadMessagesCache) {
577
+ for (const row of rows) {
578
+ cachedTargets.set(row.id, { threadId, row });
579
+ }
580
+ }
581
+ for (const includeItem of include) {
582
+ let targetThreadId;
583
+ let target;
584
+ const cached = cachedTargets.get(includeItem.id);
585
+ if (cached) {
586
+ target = cached.row;
587
+ targetThreadId = cached.threadId;
588
+ }
589
+ if (!target) {
590
+ const messageRows = await this.#db.queryTable(storage.TABLE_MESSAGES, [
591
+ { field: "id", value: includeItem.id }
592
+ ]);
593
+ if (messageRows.length > 0) {
594
+ target = messageRows[0];
595
+ targetThreadId = target.thread_id;
596
+ if (targetThreadId && !threadMessagesCache.has(targetThreadId)) {
597
+ const otherThreadRows = await this.#db.queryTable(storage.TABLE_MESSAGES, [
598
+ { field: "thread_id", value: targetThreadId }
599
+ ]);
600
+ threadMessagesCache.set(targetThreadId, otherThreadRows);
601
+ for (const row of otherThreadRows) {
602
+ cachedTargets.set(row.id, { threadId: targetThreadId, row });
603
+ }
604
+ }
605
+ }
606
+ }
607
+ if (!target || !targetThreadId) continue;
608
+ if (!messageIds.has(target.id)) {
609
+ messages.push(this.parseStoredMessage(target));
610
+ messageIds.add(target.id);
611
+ }
612
+ const targetThreadRows = threadMessagesCache.get(targetThreadId) || [];
613
+ await this.addContextMessages({
614
+ includeItem,
615
+ allMessages: targetThreadRows,
616
+ targetThreadId,
617
+ messageIds,
618
+ messages
619
+ });
620
+ }
621
+ return messages;
622
+ }
581
623
  parseStoredMessage(message) {
582
624
  const content = storage.safelyParseJSON(message.content);
583
625
  return {