@mastra/mongodb 0.0.0-working-memory-per-user-20250620163010 → 0.0.0-zod-v4-compat-part-2-20250822105954

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +251 -3
  2. package/LICENSE.md +11 -42
  3. package/dist/index.cjs +1846 -505
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +5 -7
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +1815 -474
  8. package/dist/index.js.map +1 -0
  9. package/dist/storage/MongoDBConnector.d.ts +23 -0
  10. package/dist/storage/MongoDBConnector.d.ts.map +1 -0
  11. package/dist/storage/connectors/MongoDBConnector.d.ts +23 -0
  12. package/dist/storage/connectors/MongoDBConnector.d.ts.map +1 -0
  13. package/dist/storage/connectors/base.d.ts +6 -0
  14. package/dist/storage/connectors/base.d.ts.map +1 -0
  15. package/dist/storage/domains/legacy-evals/index.d.ts +18 -0
  16. package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
  17. package/dist/storage/domains/memory/index.d.ts +80 -0
  18. package/dist/storage/domains/memory/index.d.ts.map +1 -0
  19. package/dist/storage/domains/operations/index.d.ts +38 -0
  20. package/dist/storage/domains/operations/index.d.ts.map +1 -0
  21. package/dist/storage/domains/scores/index.d.ts +41 -0
  22. package/dist/storage/domains/scores/index.d.ts.map +1 -0
  23. package/dist/storage/domains/traces/index.d.ts +18 -0
  24. package/dist/storage/domains/traces/index.d.ts.map +1 -0
  25. package/dist/storage/domains/utils.d.ts +8 -0
  26. package/dist/storage/domains/utils.d.ts.map +1 -0
  27. package/dist/storage/domains/workflows/index.d.ts +33 -0
  28. package/dist/storage/domains/workflows/index.d.ts.map +1 -0
  29. package/dist/storage/index.d.ts +178 -0
  30. package/dist/storage/index.d.ts.map +1 -0
  31. package/dist/storage/types.d.ts +11 -0
  32. package/dist/storage/types.d.ts.map +1 -0
  33. package/dist/vector/filter.d.ts +21 -0
  34. package/dist/vector/filter.d.ts.map +1 -0
  35. package/dist/vector/index.d.ts +78 -0
  36. package/dist/vector/index.d.ts.map +1 -0
  37. package/dist/vector/prompt.d.ts +6 -0
  38. package/dist/vector/prompt.d.ts.map +1 -0
  39. package/docker-compose.yaml +1 -1
  40. package/package.json +10 -10
  41. package/src/index.ts +1 -0
  42. package/src/storage/MongoDBConnector.ts +93 -0
  43. package/src/storage/connectors/MongoDBConnector.ts +93 -0
  44. package/src/storage/connectors/base.ts +7 -0
  45. package/src/storage/domains/legacy-evals/index.ts +193 -0
  46. package/src/storage/domains/memory/index.ts +741 -0
  47. package/src/storage/domains/operations/index.ts +155 -0
  48. package/src/storage/domains/scores/index.ts +379 -0
  49. package/src/storage/domains/traces/index.ts +142 -0
  50. package/src/storage/domains/utils.ts +43 -0
  51. package/src/storage/domains/workflows/index.ts +196 -0
  52. package/src/storage/index.test.ts +27 -989
  53. package/src/storage/index.ts +241 -605
  54. package/src/storage/types.ts +14 -0
  55. package/src/vector/filter.test.ts +40 -30
  56. package/src/vector/filter.ts +25 -4
  57. package/src/vector/index.test.ts +48 -3
  58. package/src/vector/index.ts +301 -131
  59. package/tsconfig.build.json +9 -0
  60. package/tsconfig.json +1 -1
  61. package/tsup.config.ts +22 -0
  62. package/dist/_tsup-dts-rollup.d.cts +0 -274
  63. package/dist/_tsup-dts-rollup.d.ts +0 -274
  64. package/dist/index.d.cts +0 -7
@@ -1,203 +1,180 @@
1
- import { MessageList } from '@mastra/core/agent';
2
1
  import type { MastraMessageContentV2 } from '@mastra/core/agent';
3
- import type { MetricResult, TestInfo } from '@mastra/core/eval';
2
+ import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
4
3
  import type { MastraMessageV1, MastraMessageV2, StorageThreadType } from '@mastra/core/memory';
4
+ import type { ScoreRowData } from '@mastra/core/scores';
5
5
  import type {
6
6
  EvalRow,
7
+ PaginationArgs,
7
8
  PaginationInfo,
8
9
  StorageColumn,
10
+ StorageDomains,
9
11
  StorageGetMessagesArg,
10
12
  StorageGetTracesArg,
13
+ StorageGetTracesPaginatedArg,
14
+ StoragePagination,
15
+ StorageResourceType,
11
16
  TABLE_NAMES,
12
17
  WorkflowRun,
18
+ WorkflowRuns,
13
19
  } from '@mastra/core/storage';
14
- import {
15
- MastraStorage,
16
- TABLE_EVALS,
17
- TABLE_MESSAGES,
18
- TABLE_THREADS,
19
- TABLE_TRACES,
20
- TABLE_WORKFLOW_SNAPSHOT,
21
- } from '@mastra/core/storage';
20
+ import { MastraStorage } from '@mastra/core/storage';
22
21
  import type { Trace } from '@mastra/core/telemetry';
23
22
  import type { WorkflowRunState } from '@mastra/core/workflows';
24
- import type { Db, MongoClientOptions } from 'mongodb';
25
- import { MongoClient } from 'mongodb';
26
-
27
- function safelyParseJSON(jsonString: string): any {
23
+ import { MongoDBConnector } from './connectors/MongoDBConnector';
24
+ import { LegacyEvalsMongoDB } from './domains/legacy-evals';
25
+ import { MemoryStorageMongoDB } from './domains/memory';
26
+ import { StoreOperationsMongoDB } from './domains/operations';
27
+ import { ScoresStorageMongoDB } from './domains/scores';
28
+ import { TracesStorageMongoDB } from './domains/traces';
29
+ import { WorkflowsStorageMongoDB } from './domains/workflows';
30
+ import type { MongoDBConfig } from './types';
31
+
32
+ const loadConnector = (config: MongoDBConfig): MongoDBConnector => {
28
33
  try {
29
- return JSON.parse(jsonString);
30
- } catch {
31
- return {};
34
+ if ('connectorHandler' in config) {
35
+ return MongoDBConnector.fromConnectionHandler(config.connectorHandler);
36
+ }
37
+ } catch (error) {
38
+ throw new MastraError(
39
+ {
40
+ id: 'STORAGE_MONGODB_STORE_CONSTRUCTOR_FAILED',
41
+ domain: ErrorDomain.STORAGE,
42
+ category: ErrorCategory.USER,
43
+ details: { connectionHandler: true },
44
+ },
45
+ error,
46
+ );
32
47
  }
33
- }
34
48
 
35
- export interface MongoDBConfig {
36
- url: string;
37
- dbName: string;
38
- options?: MongoClientOptions;
39
- }
49
+ try {
50
+ return MongoDBConnector.fromDatabaseConfig({
51
+ options: config.options,
52
+ url: config.url,
53
+ dbName: config.dbName,
54
+ });
55
+ } catch (error) {
56
+ throw new MastraError(
57
+ {
58
+ id: 'STORAGE_MONGODB_STORE_CONSTRUCTOR_FAILED',
59
+ domain: ErrorDomain.STORAGE,
60
+ category: ErrorCategory.USER,
61
+ details: { url: config?.url, dbName: config?.dbName },
62
+ },
63
+ error,
64
+ );
65
+ }
66
+ };
40
67
 
41
68
  export class MongoDBStore extends MastraStorage {
42
- #isConnected = false;
43
- #client: MongoClient;
44
- #db: Db | undefined;
45
- readonly #dbName: string;
69
+ #connector: MongoDBConnector;
70
+
71
+ stores: StorageDomains;
72
+
73
+ public get supports(): {
74
+ selectByIncludeResourceScope: boolean;
75
+ resourceWorkingMemory: boolean;
76
+ hasColumn: boolean;
77
+ createTable: boolean;
78
+ deleteMessages: boolean;
79
+ } {
80
+ return {
81
+ selectByIncludeResourceScope: true,
82
+ resourceWorkingMemory: true,
83
+ hasColumn: false,
84
+ createTable: false,
85
+ deleteMessages: false,
86
+ };
87
+ }
46
88
 
47
89
  constructor(config: MongoDBConfig) {
48
90
  super({ name: 'MongoDBStore' });
49
- this.#isConnected = false;
50
91
 
51
- if (!config.url?.trim().length) {
52
- throw new Error(
53
- 'MongoDBStore: url must be provided and cannot be empty. Passing an empty string may cause fallback to local MongoDB defaults.',
54
- );
55
- }
92
+ this.stores = {} as StorageDomains;
56
93
 
57
- if (!config.dbName?.trim().length) {
58
- throw new Error(
59
- 'MongoDBStore: dbName must be provided and cannot be empty. Passing an empty string may cause fallback to local MongoDB defaults.',
60
- );
61
- }
94
+ this.#connector = loadConnector(config);
62
95
 
63
- this.#dbName = config.dbName;
64
- this.#client = new MongoClient(config.url, config.options);
65
- }
96
+ const operations = new StoreOperationsMongoDB({
97
+ connector: this.#connector,
98
+ });
66
99
 
67
- private async getConnection(): Promise<Db> {
68
- if (this.#isConnected) {
69
- return this.#db!;
70
- }
100
+ const memory = new MemoryStorageMongoDB({
101
+ operations,
102
+ });
71
103
 
72
- await this.#client.connect();
73
- this.#db = this.#client.db(this.#dbName);
74
- this.#isConnected = true;
75
- return this.#db;
76
- }
104
+ const traces = new TracesStorageMongoDB({
105
+ operations,
106
+ });
107
+
108
+ const legacyEvals = new LegacyEvalsMongoDB({
109
+ operations,
110
+ });
111
+
112
+ const scores = new ScoresStorageMongoDB({
113
+ operations,
114
+ });
115
+
116
+ const workflows = new WorkflowsStorageMongoDB({
117
+ operations,
118
+ });
77
119
 
78
- private async getCollection(collectionName: string) {
79
- const db = await this.getConnection();
80
- return db.collection(collectionName);
120
+ this.stores = {
121
+ operations,
122
+ memory,
123
+ traces,
124
+ legacyEvals,
125
+ scores,
126
+ workflows,
127
+ };
81
128
  }
82
129
 
83
- async createTable(): Promise<void> {
84
- // Nothing to do here, MongoDB is schemaless
130
+ async createTable({
131
+ tableName,
132
+ schema,
133
+ }: {
134
+ tableName: TABLE_NAMES;
135
+ schema: Record<string, StorageColumn>;
136
+ }): Promise<void> {
137
+ return this.stores.operations.createTable({ tableName, schema });
85
138
  }
86
139
 
87
- /**
88
- * No-op: This backend is schemaless and does not require schema changes.
89
- * @param tableName Name of the table
90
- * @param schema Schema of the table
91
- * @param ifNotExists Array of column names to add if they don't exist
92
- */
93
140
  async alterTable(_args: {
94
141
  tableName: TABLE_NAMES;
95
142
  schema: Record<string, StorageColumn>;
96
143
  ifNotExists: string[];
97
144
  }): Promise<void> {
98
- // Nothing to do here, MongoDB is schemaless
145
+ return this.stores.operations.alterTable(_args);
146
+ }
147
+
148
+ async dropTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
149
+ return this.stores.operations.dropTable({ tableName });
99
150
  }
100
151
 
101
152
  async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
102
- try {
103
- const collection = await this.getCollection(tableName);
104
- await collection.deleteMany({});
105
- } catch (error) {
106
- if (error instanceof Error) {
107
- this.logger.error(error.message);
108
- }
109
- }
153
+ return this.stores.operations.clearTable({ tableName });
110
154
  }
111
155
 
112
156
  async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
113
- try {
114
- const collection = await this.getCollection(tableName);
115
- await collection.insertOne(record);
116
- } catch (error) {
117
- this.logger.error(`Error upserting into table ${tableName}: ${error}`);
118
- throw error;
119
- }
157
+ return this.stores.operations.insert({ tableName, record });
120
158
  }
121
159
 
122
160
  async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
123
- if (!records.length) {
124
- return;
125
- }
126
-
127
- try {
128
- const collection = await this.getCollection(tableName);
129
- await collection.insertMany(records);
130
- } catch (error) {
131
- this.logger.error(`Error upserting into table ${tableName}: ${error}`);
132
- throw error;
133
- }
161
+ return this.stores.operations.batchInsert({ tableName, records });
134
162
  }
135
163
 
136
164
  async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
137
- this.logger.info(`Loading ${tableName} with keys ${JSON.stringify(keys)}`);
138
- try {
139
- const collection = await this.getCollection(tableName);
140
- return (await collection.find(keys).toArray()) as R;
141
- } catch (error) {
142
- this.logger.error(`Error loading ${tableName} with keys ${JSON.stringify(keys)}: ${error}`);
143
- throw error;
144
- }
165
+ return this.stores.operations.load({ tableName, keys });
145
166
  }
146
167
 
147
168
  async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
148
- try {
149
- const collection = await this.getCollection(TABLE_THREADS);
150
- const result = await collection.findOne<any>({ id: threadId });
151
- if (!result) {
152
- return null;
153
- }
154
-
155
- return {
156
- ...result,
157
- metadata: typeof result.metadata === 'string' ? JSON.parse(result.metadata) : result.metadata,
158
- };
159
- } catch (error) {
160
- this.logger.error(`Error loading thread with ID ${threadId}: ${error}`);
161
- throw error;
162
- }
169
+ return this.stores.memory.getThreadById({ threadId });
163
170
  }
164
171
 
165
172
  async getThreadsByResourceId({ resourceId }: { resourceId: string }): Promise<StorageThreadType[]> {
166
- try {
167
- const collection = await this.getCollection(TABLE_THREADS);
168
- const results = await collection.find<any>({ resourceId }).toArray();
169
- if (!results.length) {
170
- return [];
171
- }
172
-
173
- return results.map(result => ({
174
- ...result,
175
- metadata: typeof result.metadata === 'string' ? JSON.parse(result.metadata) : result.metadata,
176
- }));
177
- } catch (error) {
178
- this.logger.error(`Error loading threads by resourceId ${resourceId}: ${error}`);
179
- throw error;
180
- }
173
+ return this.stores.memory.getThreadsByResourceId({ resourceId });
181
174
  }
182
175
 
183
176
  async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
184
- try {
185
- const collection = await this.getCollection(TABLE_THREADS);
186
- await collection.updateOne(
187
- { id: thread.id },
188
- {
189
- $set: {
190
- ...thread,
191
- metadata: JSON.stringify(thread.metadata),
192
- },
193
- },
194
- { upsert: true },
195
- );
196
- return thread;
197
- } catch (error) {
198
- this.logger.error(`Error saving thread ${thread.id}: ${error}`);
199
- throw error;
200
- }
177
+ return this.stores.memory.saveThread({ thread });
201
178
  }
202
179
 
203
180
  async updateThread({
@@ -209,51 +186,11 @@ export class MongoDBStore extends MastraStorage {
209
186
  title: string;
210
187
  metadata: Record<string, unknown>;
211
188
  }): Promise<StorageThreadType> {
212
- const thread = await this.getThreadById({ threadId: id });
213
- if (!thread) {
214
- throw new Error(`Thread ${id} not found`);
215
- }
216
-
217
- const updatedThread = {
218
- ...thread,
219
- title,
220
- metadata: {
221
- ...thread.metadata,
222
- ...metadata,
223
- },
224
- };
225
-
226
- try {
227
- const collection = await this.getCollection(TABLE_THREADS);
228
- await collection.updateOne(
229
- { id },
230
- {
231
- $set: {
232
- title,
233
- metadata: JSON.stringify(updatedThread.metadata),
234
- },
235
- },
236
- );
237
- } catch (error) {
238
- this.logger.error(`Error updating thread ${id}:) ${error}`);
239
- throw error;
240
- }
241
-
242
- return updatedThread;
189
+ return this.stores.memory.updateThread({ id, title, metadata });
243
190
  }
244
191
 
245
192
  async deleteThread({ threadId }: { threadId: string }): Promise<void> {
246
- try {
247
- // First, delete all messages associated with the thread
248
- const collectionMessages = await this.getCollection(TABLE_MESSAGES);
249
- await collectionMessages.deleteMany({ thread_id: threadId });
250
- // Then delete the thread itself
251
- const collectionThreads = await this.getCollection(TABLE_THREADS);
252
- await collectionThreads.deleteOne({ id: threadId });
253
- } catch (error) {
254
- this.logger.error(`Error deleting thread ${threadId}: ${error}`);
255
- throw error;
256
- }
193
+ return this.stores.memory.deleteThread({ threadId });
257
194
  }
258
195
 
259
196
  public async getMessages(args: StorageGetMessagesArg & { format?: 'v1' }): Promise<MastraMessageV1[]>;
@@ -265,309 +202,71 @@ export class MongoDBStore extends MastraStorage {
265
202
  }: StorageGetMessagesArg & {
266
203
  format?: 'v1' | 'v2';
267
204
  }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
268
- try {
269
- const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
270
- const include = selectBy?.include || [];
271
- let messages: MastraMessageV2[] = [];
272
- let allMessages: MastraMessageV2[] = [];
273
- const collection = await this.getCollection(TABLE_MESSAGES);
274
- // Get all messages from the thread ordered by creation date descending
275
- allMessages = (await collection.find({ thread_id: threadId }).sort({ createdAt: -1 }).toArray()).map((row: any) =>
276
- this.parseRow(row),
277
- );
278
-
279
- // If there are messages to include, select the messages around the included IDs
280
- if (include.length) {
281
- // Map IDs to their position in the ordered array
282
- const idToIndex = new Map<string, number>();
283
- allMessages.forEach((msg, idx) => {
284
- idToIndex.set(msg.id, idx);
285
- });
286
-
287
- const selectedIndexes = new Set<number>();
288
- for (const inc of include) {
289
- const idx = idToIndex.get(inc.id);
290
- if (idx === undefined) continue;
291
- // Previous messages
292
- for (let i = 1; i <= (inc.withPreviousMessages || 0); i++) {
293
- if (idx + i < allMessages.length) selectedIndexes.add(idx + i);
294
- }
295
- // Included message
296
- selectedIndexes.add(idx);
297
- // Next messages
298
- for (let i = 1; i <= (inc.withNextMessages || 0); i++) {
299
- if (idx - i >= 0) selectedIndexes.add(idx - i);
300
- }
301
- }
302
- // Add the selected messages, filtering out undefined
303
- messages.push(
304
- ...Array.from(selectedIndexes)
305
- .map(i => allMessages[i])
306
- .filter((m): m is MastraMessageV2 => !!m),
307
- );
308
- }
309
-
310
- // Get the remaining messages, excluding those already selected
311
- const excludeIds = new Set(messages.map(m => m.id));
312
- for (const msg of allMessages) {
313
- if (messages.length >= limit) break;
314
- if (!excludeIds.has(msg.id)) {
315
- messages.push(msg);
316
- }
317
- }
318
-
319
- // Sort all messages by creation date ascending
320
- messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
321
-
322
- const list = new MessageList().add(messages.slice(0, limit), 'memory');
323
- if (format === `v2`) return list.get.all.v2();
324
- return list.get.all.v1();
325
- } catch (error) {
326
- this.logger.error('Error getting messages:', error as Error);
327
- throw error;
328
- }
205
+ return this.stores.memory.getMessages({ threadId, selectBy, format });
329
206
  }
330
207
 
331
208
  async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
332
209
  async saveMessages(args: { messages: MastraMessageV2[]; format: 'v2' }): Promise<MastraMessageV2[]>;
333
- async saveMessages({
334
- messages,
335
- format,
336
- }:
337
- | { messages: MastraMessageV1[]; format?: undefined | 'v1' }
338
- | { messages: MastraMessageV2[]; format: 'v2' }): Promise<MastraMessageV2[] | MastraMessageV1[]> {
339
- if (!messages.length) {
340
- return messages;
341
- }
342
-
343
- const threadId = messages[0]?.threadId;
344
- if (!threadId) {
345
- this.logger.error('Thread ID is required to save messages');
346
- throw new Error('Thread ID is required');
347
- }
210
+ async saveMessages(
211
+ args: { messages: MastraMessageV1[]; format?: undefined | 'v1' } | { messages: MastraMessageV2[]; format: 'v2' },
212
+ ): Promise<MastraMessageV2[] | MastraMessageV1[]> {
213
+ return this.stores.memory.saveMessages(args);
214
+ }
348
215
 
349
- try {
350
- // Prepare batch statements for all messages
351
- const messagesToInsert = messages.map(message => {
352
- const time = message.createdAt || new Date();
353
- return {
354
- id: message.id,
355
- thread_id: threadId,
356
- content: typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
357
- role: message.role,
358
- type: message.type,
359
- resourceId: message.resourceId,
360
- createdAt: time instanceof Date ? time.toISOString() : time,
361
- };
362
- });
363
-
364
- // Execute message inserts and thread update in parallel for better performance
365
- const collection = await this.getCollection(TABLE_MESSAGES);
366
- const threadsCollection = await this.getCollection(TABLE_THREADS);
367
-
368
- await Promise.all([
369
- collection.insertMany(messagesToInsert),
370
- threadsCollection.updateOne({ id: threadId }, { $set: { updatedAt: new Date() } }),
371
- ]);
372
-
373
- const list = new MessageList().add(messages, 'memory');
374
- if (format === `v2`) return list.get.all.v2();
375
- return list.get.all.v1();
376
- } catch (error) {
377
- this.logger.error('Failed to save messages in database: ' + (error as { message: string })?.message);
378
- throw error;
379
- }
216
+ async getThreadsByResourceIdPaginated(_args: {
217
+ resourceId: string;
218
+ page: number;
219
+ perPage: number;
220
+ }): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
221
+ return this.stores.memory.getThreadsByResourceIdPaginated(_args);
380
222
  }
381
223
 
382
- async getTraces(
383
- {
384
- name,
385
- scope,
386
- page,
387
- perPage,
388
- attributes,
389
- filters,
390
- }: {
391
- name?: string;
392
- scope?: string;
393
- page: number;
394
- perPage: number;
395
- attributes?: Record<string, string>;
396
- filters?: Record<string, any>;
397
- } = {
398
- page: 0,
399
- perPage: 100,
400
- },
401
- ): Promise<any[]> {
402
- const limit = perPage;
403
- const offset = page * perPage;
404
-
405
- const query: any = {};
406
- if (name) {
407
- query['name'] = `%${name}%`;
408
- }
224
+ async getMessagesPaginated(
225
+ _args: StorageGetMessagesArg,
226
+ ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
227
+ return this.stores.memory.getMessagesPaginated(_args);
228
+ }
409
229
 
410
- if (scope) {
411
- query['scope'] = scope;
412
- }
230
+ async updateMessages(_args: {
231
+ messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
232
+ {
233
+ id: string;
234
+ content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
235
+ }[];
236
+ }): Promise<MastraMessageV2[]> {
237
+ return this.stores.memory.updateMessages(_args);
238
+ }
413
239
 
414
- if (attributes) {
415
- Object.keys(attributes).forEach(key => {
416
- query[`attributes.${key}`] = attributes[key];
417
- });
418
- }
240
+ async getTraces(args: StorageGetTracesArg): Promise<Trace[]> {
241
+ return this.stores.traces.getTraces(args);
242
+ }
419
243
 
420
- if (filters) {
421
- Object.entries(filters).forEach(([key, value]) => {
422
- query[key] = value;
423
- });
424
- }
244
+ async getTracesPaginated(args: StorageGetTracesPaginatedArg): Promise<PaginationInfo & { traces: Trace[] }> {
245
+ return this.stores.traces.getTracesPaginated(args);
246
+ }
425
247
 
426
- const collection = await this.getCollection(TABLE_TRACES);
427
- const result = await collection
428
- .find(query, {
429
- sort: { startTime: -1 },
430
- })
431
- .limit(limit)
432
- .skip(offset)
433
- .toArray();
434
-
435
- return result.map(row => ({
436
- id: row.id,
437
- parentSpanId: row.parentSpanId,
438
- traceId: row.traceId,
439
- name: row.name,
440
- scope: row.scope,
441
- kind: row.kind,
442
- status: safelyParseJSON(row.status as string),
443
- events: safelyParseJSON(row.events as string),
444
- links: safelyParseJSON(row.links as string),
445
- attributes: safelyParseJSON(row.attributes as string),
446
- startTime: row.startTime,
447
- endTime: row.endTime,
448
- other: safelyParseJSON(row.other as string),
449
- createdAt: row.createdAt,
450
- })) as any;
451
- }
452
-
453
- async getWorkflowRuns({
454
- workflowName,
455
- fromDate,
456
- toDate,
457
- limit,
458
- offset,
459
- }: {
248
+ async getWorkflowRuns(args?: {
460
249
  workflowName?: string;
461
250
  fromDate?: Date;
462
251
  toDate?: Date;
463
252
  limit?: number;
464
253
  offset?: number;
465
- } = {}): Promise<{
466
- runs: Array<{
467
- workflowName: string;
468
- runId: string;
469
- snapshot: WorkflowRunState | string;
470
- createdAt: Date;
471
- updatedAt: Date;
472
- }>;
473
- total: number;
474
- }> {
475
- const query: any = {};
476
- if (workflowName) {
477
- query['workflow_name'] = workflowName;
478
- }
479
-
480
- if (fromDate || toDate) {
481
- query['createdAt'] = {};
482
- if (fromDate) {
483
- query['createdAt']['$gte'] = fromDate;
484
- }
485
- if (toDate) {
486
- query['createdAt']['$lte'] = toDate;
487
- }
488
- }
489
-
490
- const collection = await this.getCollection(TABLE_WORKFLOW_SNAPSHOT);
491
- let total = 0;
492
- // Only get total count when using pagination
493
- if (limit !== undefined && offset !== undefined) {
494
- total = await collection.countDocuments(query);
495
- }
496
-
497
- // Get results
498
- const request = collection.find(query).sort({ createdAt: 'desc' });
499
- if (limit) {
500
- request.limit(limit);
501
- }
502
-
503
- if (offset) {
504
- request.skip(offset);
505
- }
506
-
507
- const result = await request.toArray();
508
- const runs = result.map(row => {
509
- let parsedSnapshot: WorkflowRunState | string = row.snapshot;
510
- if (typeof parsedSnapshot === 'string') {
511
- try {
512
- parsedSnapshot = JSON.parse(row.snapshot as string) as WorkflowRunState;
513
- } catch (e) {
514
- // If parsing fails, return the raw snapshot string
515
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
516
- }
517
- }
518
-
519
- return {
520
- workflowName: row.workflow_name as string,
521
- runId: row.run_id as string,
522
- snapshot: parsedSnapshot,
523
- createdAt: new Date(row.createdAt as string),
524
- updatedAt: new Date(row.updatedAt as string),
525
- };
526
- });
254
+ resourceId?: string;
255
+ }): Promise<WorkflowRuns> {
256
+ return this.stores.workflows.getWorkflowRuns(args);
257
+ }
527
258
 
528
- // Use runs.length as total when not paginating
529
- return { runs, total: total || runs.length };
259
+ async getEvals(
260
+ options: {
261
+ agentName?: string;
262
+ type?: 'test' | 'live';
263
+ } & PaginationArgs = {},
264
+ ): Promise<PaginationInfo & { evals: EvalRow[] }> {
265
+ return this.stores.legacyEvals.getEvals(options);
530
266
  }
531
267
 
532
268
  async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
533
- try {
534
- const query: any = {
535
- agent_name: agentName,
536
- };
537
-
538
- if (type === 'test') {
539
- query['test_info'] = { $ne: null };
540
- // is not possible to filter by test_info.testPath because it is not a json field
541
- // query['test_info.testPath'] = { $ne: null };
542
- }
543
-
544
- if (type === 'live') {
545
- // is not possible to filter by test_info.testPath because it is not a json field
546
- query['test_info'] = null;
547
- }
548
-
549
- const collection = await this.getCollection(TABLE_EVALS);
550
- const documents = await collection.find(query).sort({ created_at: 'desc' }).toArray();
551
- const result = documents.map(row => this.transformEvalRow(row));
552
- // Post filter to remove if test_info.testPath is null
553
- return result.filter(row => {
554
- if (type === 'live') {
555
- return !Boolean(row.testInfo?.testPath);
556
- }
557
-
558
- if (type === 'test') {
559
- return row.testInfo?.testPath !== null;
560
- }
561
- return true;
562
- });
563
- } catch (error) {
564
- // Handle case where table doesn't exist yet
565
- if (error instanceof Error && error.message.includes('no such table')) {
566
- return [];
567
- }
568
- this.logger.error('Failed to get evals for the specified agent: ' + (error as any)?.message);
569
- throw error;
570
- }
269
+ return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
571
270
  }
572
271
 
573
272
  async persistWorkflowSnapshot({
@@ -579,26 +278,7 @@ export class MongoDBStore extends MastraStorage {
579
278
  runId: string;
580
279
  snapshot: WorkflowRunState;
581
280
  }): Promise<void> {
582
- try {
583
- const now = new Date().toISOString();
584
- const collection = await this.getCollection(TABLE_WORKFLOW_SNAPSHOT);
585
- await collection.updateOne(
586
- { workflow_name: workflowName, run_id: runId },
587
- {
588
- $set: {
589
- snapshot: JSON.stringify(snapshot),
590
- updatedAt: now,
591
- },
592
- $setOnInsert: {
593
- createdAt: now,
594
- },
595
- },
596
- { upsert: true },
597
- );
598
- } catch (error) {
599
- this.logger.error(`Error persisting workflow snapshot: ${error}`);
600
- throw error;
601
- }
281
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
602
282
  }
603
283
 
604
284
  async loadWorkflowSnapshot({
@@ -608,24 +288,7 @@ export class MongoDBStore extends MastraStorage {
608
288
  workflowName: string;
609
289
  runId: string;
610
290
  }): Promise<WorkflowRunState | null> {
611
- try {
612
- const result = await this.load<any[]>({
613
- tableName: TABLE_WORKFLOW_SNAPSHOT,
614
- keys: {
615
- workflow_name: workflowName,
616
- run_id: runId,
617
- },
618
- });
619
-
620
- if (!result?.length) {
621
- return null;
622
- }
623
-
624
- return JSON.parse(result[0].snapshot);
625
- } catch (error) {
626
- console.error('Error loading workflow snapshot:', error);
627
- throw error;
628
- }
291
+ return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
629
292
  }
630
293
 
631
294
  async getWorkflowRunById({
@@ -635,122 +298,95 @@ export class MongoDBStore extends MastraStorage {
635
298
  runId: string;
636
299
  workflowName?: string;
637
300
  }): Promise<WorkflowRun | null> {
301
+ return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
302
+ }
303
+
304
+ async close(): Promise<void> {
638
305
  try {
639
- const query: any = {};
640
- if (runId) {
641
- query['run_id'] = runId;
642
- }
643
-
644
- if (workflowName) {
645
- query['workflow_name'] = workflowName;
646
- }
647
-
648
- const collection = await this.getCollection(TABLE_WORKFLOW_SNAPSHOT);
649
- const result = await collection.findOne(query);
650
- if (!result) {
651
- return null;
652
- }
653
-
654
- return this.parseWorkflowRun(result);
306
+ await this.#connector.close();
655
307
  } catch (error) {
656
- console.error('Error getting workflow run by ID:', error);
657
- throw error;
308
+ throw new MastraError(
309
+ {
310
+ id: 'STORAGE_MONGODB_STORE_CLOSE_FAILED',
311
+ domain: ErrorDomain.STORAGE,
312
+ category: ErrorCategory.USER,
313
+ },
314
+ error,
315
+ );
658
316
  }
659
317
  }
660
318
 
661
- private parseWorkflowRun(row: any): WorkflowRun {
662
- let parsedSnapshot: WorkflowRunState | string = row.snapshot as string;
663
- if (typeof parsedSnapshot === 'string') {
664
- try {
665
- parsedSnapshot = JSON.parse(row.snapshot as string) as WorkflowRunState;
666
- } catch (e) {
667
- // If parsing fails, return the raw snapshot string
668
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
669
- }
670
- }
671
-
672
- return {
673
- workflowName: row.workflow_name,
674
- runId: row.run_id,
675
- snapshot: parsedSnapshot,
676
- createdAt: row.createdAt,
677
- updatedAt: row.updatedAt,
678
- resourceId: row.resourceId,
679
- };
319
+ /**
320
+ * SCORERS
321
+ */
322
+ async getScoreById({ id }: { id: string }): Promise<ScoreRowData | null> {
323
+ return this.stores.scores.getScoreById({ id });
680
324
  }
681
325
 
682
- private parseRow(row: any): MastraMessageV2 {
683
- let content = row.content;
684
- try {
685
- content = JSON.parse(row.content);
686
- } catch {
687
- // use content as is if it's not JSON
688
- }
689
- return {
690
- id: row.id,
691
- content,
692
- role: row.role,
693
- type: row.type,
694
- createdAt: new Date(row.createdAt as string),
695
- threadId: row.thread_id,
696
- resourceId: row.resourceId,
697
- } as MastraMessageV2;
698
- }
699
-
700
- private transformEvalRow(row: Record<string, any>): EvalRow {
701
- let testInfoValue = null;
702
- if (row.test_info) {
703
- try {
704
- testInfoValue = typeof row.test_info === 'string' ? JSON.parse(row.test_info) : row.test_info;
705
- } catch (e) {
706
- console.warn('Failed to parse test_info:', e);
707
- }
708
- }
326
+ async saveScore(score: Omit<ScoreRowData, 'id' | 'createdAt' | 'updatedAt'>): Promise<{ score: ScoreRowData }> {
327
+ return this.stores.scores.saveScore(score);
328
+ }
709
329
 
710
- return {
711
- input: row.input as string,
712
- output: row.output as string,
713
- result: row.result as MetricResult,
714
- agentName: row.agent_name as string,
715
- metricName: row.metric_name as string,
716
- instructions: row.instructions as string,
717
- testInfo: testInfoValue as TestInfo,
718
- globalRunId: row.global_run_id as string,
719
- runId: row.run_id as string,
720
- createdAt: row.created_at as string,
721
- };
330
+ async getScoresByRunId({
331
+ runId,
332
+ pagination,
333
+ }: {
334
+ runId: string;
335
+ pagination: StoragePagination;
336
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
337
+ return this.stores.scores.getScoresByRunId({ runId, pagination });
722
338
  }
723
339
 
724
- async getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & { traces: Trace[] }> {
725
- throw new Error('Method not implemented.');
340
+ async getScoresByEntityId({
341
+ entityId,
342
+ entityType,
343
+ pagination,
344
+ }: {
345
+ pagination: StoragePagination;
346
+ entityId: string;
347
+ entityType: string;
348
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
349
+ return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
726
350
  }
727
351
 
728
- async getThreadsByResourceIdPaginated(_args: {
729
- resourceId: string;
730
- page?: number;
731
- perPage?: number;
732
- }): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
733
- throw new Error('Method not implemented.');
352
+ async getScoresByScorerId({
353
+ scorerId,
354
+ pagination,
355
+ entityId,
356
+ entityType,
357
+ }: {
358
+ scorerId: string;
359
+ pagination: StoragePagination;
360
+ entityId?: string;
361
+ entityType?: string;
362
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
363
+ return this.stores.scores.getScoresByScorerId({ scorerId, pagination, entityId, entityType });
734
364
  }
735
365
 
736
- async getMessagesPaginated(
737
- _args: StorageGetMessagesArg,
738
- ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
739
- throw new Error('Method not implemented.');
366
+ /**
367
+ * RESOURCES
368
+ */
369
+ async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
370
+ return this.stores.memory.getResourceById({ resourceId });
740
371
  }
741
372
 
742
- async close(): Promise<void> {
743
- await this.#client.close();
373
+ async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
374
+ return this.stores.memory.saveResource({ resource });
744
375
  }
745
376
 
746
- async updateMessages(_args: {
747
- messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
748
- {
749
- id: string;
750
- content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
751
- }[];
752
- }): Promise<MastraMessageV2[]> {
753
- this.logger.error('updateMessages is not yet implemented in MongoDBStore');
754
- throw new Error('Method not implemented');
377
+ async updateResource({
378
+ resourceId,
379
+ workingMemory,
380
+ metadata,
381
+ }: {
382
+ resourceId: string;
383
+ workingMemory?: string;
384
+ metadata?: Record<string, unknown>;
385
+ }): Promise<StorageResourceType> {
386
+ return this.stores.memory.updateResource({
387
+ resourceId,
388
+ workingMemory,
389
+ metadata,
390
+ });
755
391
  }
756
392
  }