@mastra/mongodb 1.20.1 → 1.22.0-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/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { MessageList } from "@mastra/core/agent";
8
8
  import { saveScorePayloadSchema } from "@mastra/core/evals";
9
9
  import { skillSnapshotFieldValuesEqual } from "@mastra/core/storage/domains/skills";
10
10
  //#region package.json
11
- var version = "1.20.1";
11
+ var version = "1.22.0-alpha.0";
12
12
  //#endregion
13
13
  //#region src/vector/filter.ts
14
14
  /**
@@ -84,11 +84,22 @@ var MongoDBFilterTranslator = class extends BaseFilterTranslator {
84
84
  function describeEmbedding(config) {
85
85
  return config ? `autoEmbed (path "${config.path}", model "${config.model}")` : "client-side vectors";
86
86
  }
87
+ /**
88
+ * Whether Atlas refused a `$vectorSearch` because the index is still in its initial sync. The
89
+ * server reports it as a generic `UnknownError`, so the state named in the message is the only
90
+ * thing that tells it apart from other failures with the same code.
91
+ */
92
+ function isInitialSyncError(error) {
93
+ const message = error?.message;
94
+ return typeof message === "string" && message.includes("while in state INITIAL_SYNC");
95
+ }
87
96
  var MongoDBVector = class MongoDBVector extends MastraVector {
88
97
  client;
89
98
  db;
90
99
  collections;
91
100
  embeddingFieldName;
101
+ /** Automated Embedding defaults applied to indexes created without their own config. */
102
+ defaultAutoEmbed;
92
103
  metadataFieldName = "metadata";
93
104
  documentFieldName = "document";
94
105
  /**
@@ -124,6 +135,19 @@ var MongoDBVector = class MongoDBVector extends MastraVector {
124
135
  */
125
136
  static REGISTRY_COLLECTION = "__mastra_vector_indexes__";
126
137
  /**
138
+ * Waits between attempts while a queried index is in INITIAL_SYNC, about six seconds in all.
139
+ * On a live Atlas cluster the window lasted around two seconds for both a regular and an
140
+ * autoEmbed index; the budget leaves room for a slower build without holding a failing query
141
+ * for long.
142
+ */
143
+ static INITIAL_SYNC_RETRY_DELAYS_MS = [
144
+ 250,
145
+ 500,
146
+ 1e3,
147
+ 2e3,
148
+ 2e3
149
+ ];
150
+ /**
127
151
  * MongoDB query operators supported inside `$vectorSearch.filter`. Intentionally
128
152
  * conservative: filters using any operator outside this set fall back to the
129
153
  * `$match` pre-filter, which supports the full query language. Widening this set
@@ -146,7 +170,7 @@ var MongoDBVector = class MongoDBVector extends MastraVector {
146
170
  euclidean: "euclidean",
147
171
  dotproduct: "dotProduct"
148
172
  };
149
- constructor({ id, uri, dbName, options, embeddingFieldPath }) {
173
+ constructor({ id, uri, dbName, options, embeddingFieldPath, autoEmbed }) {
150
174
  super({ id });
151
175
  if (!uri) throw new Error("MongoDBVector requires a connection string. Provide \"uri\" in the constructor options.");
152
176
  const client = new MongoClient(uri, {
@@ -160,6 +184,11 @@ var MongoDBVector = class MongoDBVector extends MastraVector {
160
184
  this.db = this.client.db(dbName);
161
185
  this.collections = /* @__PURE__ */ new Map();
162
186
  this.embeddingFieldName = embeddingFieldPath ?? "embedding";
187
+ this.defaultAutoEmbed = autoEmbed;
188
+ }
189
+ /** True once the store is configured with Automated Embedding defaults. */
190
+ get isSelfEmbedding() {
191
+ return this.defaultAutoEmbed !== void 0;
163
192
  }
164
193
  /**
165
194
  * Resolve the collection + vector-index names for an index, honoring BYO overrides.
@@ -441,7 +470,8 @@ var MongoDBVector = class MongoDBVector extends MastraVector {
441
470
  * "index not found" or "index not ready" errors on subsequent operations.
442
471
  */
443
472
  async createIndex(params) {
444
- const { indexName, dimension, metric = "cosine", filterFields, collectionName, searchIndexName, allowWrites, autoEmbed } = params;
473
+ const { indexName, dimension, metric = "cosine", filterFields, collectionName, searchIndexName, allowWrites, autoEmbed: autoEmbedParam } = params;
474
+ const autoEmbed = autoEmbedParam ?? (dimension === void 0 ? this.defaultAutoEmbed : void 0);
445
475
  let mongoMetric;
446
476
  try {
447
477
  if (autoEmbed) {
@@ -1029,7 +1059,7 @@ var MongoDBVector = class MongoDBVector extends MastraVector {
1029
1059
  vectorSearch.filter = pushed.length > 1 ? { $and: pushed } : pushed[0];
1030
1060
  }
1031
1061
  const pipeline = [{ $vectorSearch: vectorSearch }, ...this.buildProjection(metadataMode, includeVector, "vectorSearchScore", autoEmbed?.path)];
1032
- return (await collection.aggregate(pipeline).toArray()).map((result) => ({
1062
+ return (await this.aggregateVectorSearch(collection, pipeline)).map((result) => ({
1033
1063
  id: this.idToString(result._id),
1034
1064
  score: result.score,
1035
1065
  metadata: result.metadata,
@@ -1046,6 +1076,24 @@ var MongoDBVector = class MongoDBVector extends MastraVector {
1046
1076
  }
1047
1077
  }
1048
1078
  /**
1079
+ * Runs a `$vectorSearch` pipeline, retrying while the index is in INITIAL_SYNC.
1080
+ *
1081
+ * While Atlas first builds a vector search index it usually answers with an empty result, but
1082
+ * for a few seconds it fails with "cannot query vector index ... while in state INITIAL_SYNC"
1083
+ * instead. A caller that creates an index and queries it straight away, as `Memory` does on a
1084
+ * fresh database, can land in that window. Any other error, and an INITIAL_SYNC that outlasts
1085
+ * the retry budget, is thrown unchanged.
1086
+ */
1087
+ async aggregateVectorSearch(collection, pipeline) {
1088
+ for (const delayMs of MongoDBVector.INITIAL_SYNC_RETRY_DELAYS_MS) try {
1089
+ return await collection.aggregate(pipeline).toArray();
1090
+ } catch (error) {
1091
+ if (!isInitialSyncError(error)) throw error;
1092
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1093
+ }
1094
+ return collection.aggregate(pipeline).toArray();
1095
+ }
1096
+ /**
1049
1097
  * Lists LOGICAL Mastra index names, not physical collection names.
1050
1098
  *
1051
1099
  * Returns the union of:
@@ -6464,6 +6512,7 @@ const OM_TABLE = "mastra_observational_memory";
6464
6512
  var MemoryStorageMongoDB = class MemoryStorageMongoDB extends MemoryStorage {
6465
6513
  supportsPartialThreadUpdate = true;
6466
6514
  supportsObservationalMemory = true;
6515
+ supportsObservationalMemoryHistorySearch = true;
6467
6516
  #connector;
6468
6517
  #skipDefaultIndexes;
6469
6518
  #indexes;
@@ -6699,8 +6748,11 @@ var MemoryStorageMongoDB = class MemoryStorageMongoDB extends MemoryStorage {
6699
6748
  if (!target) continue;
6700
6749
  const prevMessages = await collection.find({
6701
6750
  thread_id: target.threadId,
6702
- createdAt: { $lte: target.createdAt },
6703
- ...resourceFilter
6751
+ ...resourceFilter,
6752
+ $or: [{ createdAt: { $lt: target.createdAt } }, {
6753
+ createdAt: target.createdAt,
6754
+ id: { $lte: id }
6755
+ }]
6704
6756
  }).sort({
6705
6757
  createdAt: -1,
6706
6758
  id: -1
@@ -6709,8 +6761,11 @@ var MemoryStorageMongoDB = class MemoryStorageMongoDB extends MemoryStorage {
6709
6761
  if (withNextMessages > 0) {
6710
6762
  const nextMessages = await collection.find({
6711
6763
  thread_id: target.threadId,
6712
- createdAt: { $gt: target.createdAt },
6713
- ...resourceFilter
6764
+ ...resourceFilter,
6765
+ $or: [{ createdAt: { $gt: target.createdAt } }, {
6766
+ createdAt: target.createdAt,
6767
+ id: { $gt: id }
6768
+ }]
6714
6769
  }).sort({
6715
6770
  createdAt: 1,
6716
6771
  id: 1
@@ -7504,13 +7559,34 @@ var MemoryStorageMongoDB = class MemoryStorageMongoDB extends MemoryStorage {
7504
7559
  const lookupKey = this.getOMKey(threadId, resourceId);
7505
7560
  const collection = await this.getCollection(OM_TABLE);
7506
7561
  const filter = { lookupKey };
7562
+ if (options?.recordId !== void 0) filter["id"] = options.recordId;
7507
7563
  if (options?.from || options?.to) {
7508
7564
  const createdAtFilter = {};
7509
7565
  if (options.from) createdAtFilter["$gte"] = options.from;
7510
7566
  if (options.to) createdAtFilter["$lte"] = options.to;
7511
7567
  filter["createdAt"] = createdAtFilter;
7512
7568
  }
7513
- let cursor = collection.find(filter).sort({ generationCount: -1 });
7569
+ if (options?.groupId !== void 0) {
7570
+ const prefix = { $literal: `<observation-group id="${options.groupId}"` };
7571
+ filter["$expr"] = { $or: [{ $gte: [{ $indexOfCP: [{ $ifNull: ["$activeObservations", ""] }, prefix] }, 0] }, { $anyElementTrue: [{ $map: {
7572
+ input: { $cond: [
7573
+ { $isArray: "$bufferedObservationChunks" },
7574
+ "$bufferedObservationChunks",
7575
+ []
7576
+ ] },
7577
+ as: "chunk",
7578
+ in: { $gte: [{ $indexOfCP: [{ $ifNull: ["$$chunk.observations", ""] }, prefix] }, 0] }
7579
+ } }] }] };
7580
+ }
7581
+ if (options?.beforeGeneration !== void 0 || options?.afterGeneration !== void 0) filter["generationCount"] = {
7582
+ ...options.beforeGeneration !== void 0 ? { $lt: options.beforeGeneration } : {},
7583
+ ...options.afterGeneration !== void 0 ? { $gt: options.afterGeneration } : {}
7584
+ };
7585
+ let cursor = collection.find(filter).sort({
7586
+ generationCount: options?.sortDirection === "ASC" ? 1 : -1,
7587
+ createdAt: 1,
7588
+ id: 1
7589
+ });
7514
7590
  if (options?.offset != null) cursor = cursor.skip(options.offset);
7515
7591
  return (await cursor.limit(limit).toArray()).map((doc) => this.parseOMDocument(doc));
7516
7592
  } catch (error) {