@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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-semantic-recall.md +35 -1
- package/dist/docs/references/docs-storage.md +1 -0
- package/dist/index.cjs +85 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +85 -9
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +1 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/vector/index.d.ts +29 -1
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +6 -5
package/dist/docs/SKILL.md
CHANGED
|
@@ -20,7 +20,7 @@ After getting a response from the LLM, all new messages (user, assistant, and to
|
|
|
20
20
|
|
|
21
21
|
## Quickstart
|
|
22
22
|
|
|
23
|
-
Semantic recall is disabled by default. To enable it, set `semanticRecall: true` in `options` and provide a `vector` store and `embedder
|
|
23
|
+
Semantic recall is disabled by default. To enable it, set `semanticRecall: true` in `options` and provide a `vector` store and an `embedder`. A store that generates embeddings itself, such as MongoDB with Automated Embedding, works without one. See [Server-side embeddings](#server-side-embeddings).
|
|
24
24
|
|
|
25
25
|
**LibSQL**:
|
|
26
26
|
|
|
@@ -242,6 +242,40 @@ const options = {
|
|
|
242
242
|
}
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
+
## Server-side embeddings
|
|
246
|
+
|
|
247
|
+
Some vector stores generate the embeddings themselves. With one of those, semantic recall runs without an `embedder`: the database embeds each message on write and the search string on arrival. The embedding provider stays out of your application entirely.
|
|
248
|
+
|
|
249
|
+
MongoDB does this through [Automated Embedding](https://mastra.ai/reference/vectors/mongodb): name a Voyage AI model on the vector store and leave the `embedder` out.
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { Memory } from '@mastra/memory'
|
|
253
|
+
import { MongoDBStore, MongoDBVector } from '@mastra/mongodb'
|
|
254
|
+
|
|
255
|
+
const memory = new Memory({
|
|
256
|
+
storage: new MongoDBStore({
|
|
257
|
+
id: 'agent-storage',
|
|
258
|
+
uri: process.env.MONGODB_URI,
|
|
259
|
+
dbName: process.env.MONGODB_DB_NAME,
|
|
260
|
+
}),
|
|
261
|
+
vector: new MongoDBVector({
|
|
262
|
+
id: 'agent-vector',
|
|
263
|
+
uri: process.env.MONGODB_URI,
|
|
264
|
+
dbName: process.env.MONGODB_DB_NAME,
|
|
265
|
+
autoEmbed: { model: 'voyage-4' },
|
|
266
|
+
}),
|
|
267
|
+
options: {
|
|
268
|
+
semanticRecall: { topK: 3, messageRange: 2 },
|
|
269
|
+
},
|
|
270
|
+
})
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Messages are stored as text and MongoDB embeds them, so the model choice and the vector size belong to the index rather than to your application. Recall works the same way through an agent and through [`recall()`](#using-the-recall-method).
|
|
274
|
+
|
|
275
|
+
MongoDB embeds newly written messages in the background. A message saved moments ago may not be recallable immediately.
|
|
276
|
+
|
|
277
|
+
Every other store expects your application to supply the vectors, and keeps requiring an `embedder`. Enabling semantic recall with neither an `embedder` nor a store that embeds server-side raises an error naming both options.
|
|
278
|
+
|
|
245
279
|
## Embedder configuration
|
|
246
280
|
|
|
247
281
|
Semantic recall relies on an [embedding model](https://mastra.ai/reference/memory/memory-class) to convert messages into embeddings. Mastra supports embedding models through the model router using `provider/model` strings, or you can use any [embedding model](https://sdk.vercel.ai/docs/ai-sdk-core/embeddings) compatible with the AI SDK.
|
|
@@ -194,6 +194,7 @@ Each provider page includes installation instructions, configuration parameters,
|
|
|
194
194
|
|
|
195
195
|
- [Aurora DSQL](https://mastra.ai/integrations/databases/aurora-dsql)
|
|
196
196
|
- [ClickHouse](https://mastra.ai/integrations/databases/clickhouse)
|
|
197
|
+
- [ClickHouse Managed Postgres](https://mastra.ai/integrations/databases/clickhouse-managed-postgres)
|
|
197
198
|
- [Cloudflare D1](https://mastra.ai/integrations/databases/cloudflare-d1)
|
|
198
199
|
- [Cloudflare KV](https://mastra.ai/integrations/databases/cloudflare-kv)
|
|
199
200
|
- [Convex](https://mastra.ai/integrations/databases/convex)
|
package/dist/index.cjs
CHANGED
|
@@ -9,7 +9,7 @@ let _mastra_core_agent = require("@mastra/core/agent");
|
|
|
9
9
|
let _mastra_core_evals = require("@mastra/core/evals");
|
|
10
10
|
let _mastra_core_storage_domains_skills = require("@mastra/core/storage/domains/skills");
|
|
11
11
|
//#region package.json
|
|
12
|
-
var version = "1.
|
|
12
|
+
var version = "1.22.0-alpha.0";
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/vector/filter.ts
|
|
15
15
|
/**
|
|
@@ -85,11 +85,22 @@ var MongoDBFilterTranslator = class extends _mastra_core_vector_filter.BaseFilte
|
|
|
85
85
|
function describeEmbedding(config) {
|
|
86
86
|
return config ? `autoEmbed (path "${config.path}", model "${config.model}")` : "client-side vectors";
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Whether Atlas refused a `$vectorSearch` because the index is still in its initial sync. The
|
|
90
|
+
* server reports it as a generic `UnknownError`, so the state named in the message is the only
|
|
91
|
+
* thing that tells it apart from other failures with the same code.
|
|
92
|
+
*/
|
|
93
|
+
function isInitialSyncError(error) {
|
|
94
|
+
const message = error?.message;
|
|
95
|
+
return typeof message === "string" && message.includes("while in state INITIAL_SYNC");
|
|
96
|
+
}
|
|
88
97
|
var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector {
|
|
89
98
|
client;
|
|
90
99
|
db;
|
|
91
100
|
collections;
|
|
92
101
|
embeddingFieldName;
|
|
102
|
+
/** Automated Embedding defaults applied to indexes created without their own config. */
|
|
103
|
+
defaultAutoEmbed;
|
|
93
104
|
metadataFieldName = "metadata";
|
|
94
105
|
documentFieldName = "document";
|
|
95
106
|
/**
|
|
@@ -125,6 +136,19 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
|
|
|
125
136
|
*/
|
|
126
137
|
static REGISTRY_COLLECTION = "__mastra_vector_indexes__";
|
|
127
138
|
/**
|
|
139
|
+
* Waits between attempts while a queried index is in INITIAL_SYNC, about six seconds in all.
|
|
140
|
+
* On a live Atlas cluster the window lasted around two seconds for both a regular and an
|
|
141
|
+
* autoEmbed index; the budget leaves room for a slower build without holding a failing query
|
|
142
|
+
* for long.
|
|
143
|
+
*/
|
|
144
|
+
static INITIAL_SYNC_RETRY_DELAYS_MS = [
|
|
145
|
+
250,
|
|
146
|
+
500,
|
|
147
|
+
1e3,
|
|
148
|
+
2e3,
|
|
149
|
+
2e3
|
|
150
|
+
];
|
|
151
|
+
/**
|
|
128
152
|
* MongoDB query operators supported inside `$vectorSearch.filter`. Intentionally
|
|
129
153
|
* conservative: filters using any operator outside this set fall back to the
|
|
130
154
|
* `$match` pre-filter, which supports the full query language. Widening this set
|
|
@@ -147,7 +171,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
|
|
|
147
171
|
euclidean: "euclidean",
|
|
148
172
|
dotproduct: "dotProduct"
|
|
149
173
|
};
|
|
150
|
-
constructor({ id, uri, dbName, options, embeddingFieldPath }) {
|
|
174
|
+
constructor({ id, uri, dbName, options, embeddingFieldPath, autoEmbed }) {
|
|
151
175
|
super({ id });
|
|
152
176
|
if (!uri) throw new Error("MongoDBVector requires a connection string. Provide \"uri\" in the constructor options.");
|
|
153
177
|
const client = new mongodb.MongoClient(uri, {
|
|
@@ -161,6 +185,11 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
|
|
|
161
185
|
this.db = this.client.db(dbName);
|
|
162
186
|
this.collections = /* @__PURE__ */ new Map();
|
|
163
187
|
this.embeddingFieldName = embeddingFieldPath ?? "embedding";
|
|
188
|
+
this.defaultAutoEmbed = autoEmbed;
|
|
189
|
+
}
|
|
190
|
+
/** True once the store is configured with Automated Embedding defaults. */
|
|
191
|
+
get isSelfEmbedding() {
|
|
192
|
+
return this.defaultAutoEmbed !== void 0;
|
|
164
193
|
}
|
|
165
194
|
/**
|
|
166
195
|
* Resolve the collection + vector-index names for an index, honoring BYO overrides.
|
|
@@ -442,7 +471,8 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
|
|
|
442
471
|
* "index not found" or "index not ready" errors on subsequent operations.
|
|
443
472
|
*/
|
|
444
473
|
async createIndex(params) {
|
|
445
|
-
const { indexName, dimension, metric = "cosine", filterFields, collectionName, searchIndexName, allowWrites, autoEmbed } = params;
|
|
474
|
+
const { indexName, dimension, metric = "cosine", filterFields, collectionName, searchIndexName, allowWrites, autoEmbed: autoEmbedParam } = params;
|
|
475
|
+
const autoEmbed = autoEmbedParam ?? (dimension === void 0 ? this.defaultAutoEmbed : void 0);
|
|
446
476
|
let mongoMetric;
|
|
447
477
|
try {
|
|
448
478
|
if (autoEmbed) {
|
|
@@ -1030,7 +1060,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
|
|
|
1030
1060
|
vectorSearch.filter = pushed.length > 1 ? { $and: pushed } : pushed[0];
|
|
1031
1061
|
}
|
|
1032
1062
|
const pipeline = [{ $vectorSearch: vectorSearch }, ...this.buildProjection(metadataMode, includeVector, "vectorSearchScore", autoEmbed?.path)];
|
|
1033
|
-
return (await
|
|
1063
|
+
return (await this.aggregateVectorSearch(collection, pipeline)).map((result) => ({
|
|
1034
1064
|
id: this.idToString(result._id),
|
|
1035
1065
|
score: result.score,
|
|
1036
1066
|
metadata: result.metadata,
|
|
@@ -1047,6 +1077,24 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
|
|
|
1047
1077
|
}
|
|
1048
1078
|
}
|
|
1049
1079
|
/**
|
|
1080
|
+
* Runs a `$vectorSearch` pipeline, retrying while the index is in INITIAL_SYNC.
|
|
1081
|
+
*
|
|
1082
|
+
* While Atlas first builds a vector search index it usually answers with an empty result, but
|
|
1083
|
+
* for a few seconds it fails with "cannot query vector index ... while in state INITIAL_SYNC"
|
|
1084
|
+
* instead. A caller that creates an index and queries it straight away, as `Memory` does on a
|
|
1085
|
+
* fresh database, can land in that window. Any other error, and an INITIAL_SYNC that outlasts
|
|
1086
|
+
* the retry budget, is thrown unchanged.
|
|
1087
|
+
*/
|
|
1088
|
+
async aggregateVectorSearch(collection, pipeline) {
|
|
1089
|
+
for (const delayMs of MongoDBVector.INITIAL_SYNC_RETRY_DELAYS_MS) try {
|
|
1090
|
+
return await collection.aggregate(pipeline).toArray();
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
if (!isInitialSyncError(error)) throw error;
|
|
1093
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1094
|
+
}
|
|
1095
|
+
return collection.aggregate(pipeline).toArray();
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1050
1098
|
* Lists LOGICAL Mastra index names, not physical collection names.
|
|
1051
1099
|
*
|
|
1052
1100
|
* Returns the union of:
|
|
@@ -6465,6 +6513,7 @@ const OM_TABLE = "mastra_observational_memory";
|
|
|
6465
6513
|
var MemoryStorageMongoDB = class MemoryStorageMongoDB extends _mastra_core_storage.MemoryStorage {
|
|
6466
6514
|
supportsPartialThreadUpdate = true;
|
|
6467
6515
|
supportsObservationalMemory = true;
|
|
6516
|
+
supportsObservationalMemoryHistorySearch = true;
|
|
6468
6517
|
#connector;
|
|
6469
6518
|
#skipDefaultIndexes;
|
|
6470
6519
|
#indexes;
|
|
@@ -6700,8 +6749,11 @@ var MemoryStorageMongoDB = class MemoryStorageMongoDB extends _mastra_core_stora
|
|
|
6700
6749
|
if (!target) continue;
|
|
6701
6750
|
const prevMessages = await collection.find({
|
|
6702
6751
|
thread_id: target.threadId,
|
|
6703
|
-
|
|
6704
|
-
|
|
6752
|
+
...resourceFilter,
|
|
6753
|
+
$or: [{ createdAt: { $lt: target.createdAt } }, {
|
|
6754
|
+
createdAt: target.createdAt,
|
|
6755
|
+
id: { $lte: id }
|
|
6756
|
+
}]
|
|
6705
6757
|
}).sort({
|
|
6706
6758
|
createdAt: -1,
|
|
6707
6759
|
id: -1
|
|
@@ -6710,8 +6762,11 @@ var MemoryStorageMongoDB = class MemoryStorageMongoDB extends _mastra_core_stora
|
|
|
6710
6762
|
if (withNextMessages > 0) {
|
|
6711
6763
|
const nextMessages = await collection.find({
|
|
6712
6764
|
thread_id: target.threadId,
|
|
6713
|
-
|
|
6714
|
-
|
|
6765
|
+
...resourceFilter,
|
|
6766
|
+
$or: [{ createdAt: { $gt: target.createdAt } }, {
|
|
6767
|
+
createdAt: target.createdAt,
|
|
6768
|
+
id: { $gt: id }
|
|
6769
|
+
}]
|
|
6715
6770
|
}).sort({
|
|
6716
6771
|
createdAt: 1,
|
|
6717
6772
|
id: 1
|
|
@@ -7505,13 +7560,34 @@ var MemoryStorageMongoDB = class MemoryStorageMongoDB extends _mastra_core_stora
|
|
|
7505
7560
|
const lookupKey = this.getOMKey(threadId, resourceId);
|
|
7506
7561
|
const collection = await this.getCollection(OM_TABLE);
|
|
7507
7562
|
const filter = { lookupKey };
|
|
7563
|
+
if (options?.recordId !== void 0) filter["id"] = options.recordId;
|
|
7508
7564
|
if (options?.from || options?.to) {
|
|
7509
7565
|
const createdAtFilter = {};
|
|
7510
7566
|
if (options.from) createdAtFilter["$gte"] = options.from;
|
|
7511
7567
|
if (options.to) createdAtFilter["$lte"] = options.to;
|
|
7512
7568
|
filter["createdAt"] = createdAtFilter;
|
|
7513
7569
|
}
|
|
7514
|
-
|
|
7570
|
+
if (options?.groupId !== void 0) {
|
|
7571
|
+
const prefix = { $literal: `<observation-group id="${options.groupId}"` };
|
|
7572
|
+
filter["$expr"] = { $or: [{ $gte: [{ $indexOfCP: [{ $ifNull: ["$activeObservations", ""] }, prefix] }, 0] }, { $anyElementTrue: [{ $map: {
|
|
7573
|
+
input: { $cond: [
|
|
7574
|
+
{ $isArray: "$bufferedObservationChunks" },
|
|
7575
|
+
"$bufferedObservationChunks",
|
|
7576
|
+
[]
|
|
7577
|
+
] },
|
|
7578
|
+
as: "chunk",
|
|
7579
|
+
in: { $gte: [{ $indexOfCP: [{ $ifNull: ["$$chunk.observations", ""] }, prefix] }, 0] }
|
|
7580
|
+
} }] }] };
|
|
7581
|
+
}
|
|
7582
|
+
if (options?.beforeGeneration !== void 0 || options?.afterGeneration !== void 0) filter["generationCount"] = {
|
|
7583
|
+
...options.beforeGeneration !== void 0 ? { $lt: options.beforeGeneration } : {},
|
|
7584
|
+
...options.afterGeneration !== void 0 ? { $gt: options.afterGeneration } : {}
|
|
7585
|
+
};
|
|
7586
|
+
let cursor = collection.find(filter).sort({
|
|
7587
|
+
generationCount: options?.sortDirection === "ASC" ? 1 : -1,
|
|
7588
|
+
createdAt: 1,
|
|
7589
|
+
id: 1
|
|
7590
|
+
});
|
|
7515
7591
|
if (options?.offset != null) cursor = cursor.skip(options.offset);
|
|
7516
7592
|
return (await cursor.limit(limit).toArray()).map((doc) => this.parseOMDocument(doc));
|
|
7517
7593
|
} catch (error) {
|