@mastra/rag 2.5.0 → 2.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @mastra/rag
2
2
 
3
+ ## 2.6.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added `serialize()` and `GraphRAG.deserialize()` so a knowledge graph can be saved and restored instead of rebuilt on every process start. Building a graph compares every chunk against every other chunk, which is slow for large document sets; now you can do that work once and reload the result. ([#21704](https://github.com/mastra-ai/mastra/pull/21704))
8
+
9
+ ```typescript
10
+ // Before: the graph had to be rebuilt every time
11
+ const graphRag = new GraphRAG(1536, 0.7);
12
+ graphRag.createGraph(documentChunks, embeddings);
13
+
14
+ // After: build once, save the snapshot, and reload it later
15
+ const snapshot = graphRag.serialize();
16
+ await writeFile('./graph.json', JSON.stringify(snapshot));
17
+
18
+ const restored = GraphRAG.deserialize(JSON.parse(await readFile('./graph.json', 'utf8')));
19
+ restored.query({ query: queryEmbedding, topK: 10 });
20
+ ```
21
+
22
+ A snapshot is plain JSON, so you can store it in any database, file, or cache you already use. Loading a snapshot that does not match the graph's embedding dimension now fails immediately with a clear error instead of later during a query. Closes #3926.
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies [[`b0a2a07`](https://github.com/mastra-ai/mastra/commit/b0a2a07800d42bd9823292e7db832374ed084c9c), [`ccbbcd9`](https://github.com/mastra-ai/mastra/commit/ccbbcd974eedff4367a54ed0e24c9ee742ab2f61), [`3f5c6f7`](https://github.com/mastra-ai/mastra/commit/3f5c6f728ea35da344248de9aa070f12849f3aa0), [`77e6b1b`](https://github.com/mastra-ai/mastra/commit/77e6b1bc4c46ce94fe501023fb4393c812ec6be3), [`2e1d098`](https://github.com/mastra-ai/mastra/commit/2e1d0984e325fd319d32ea182f596b3170be3847)]:
27
+ - @mastra/core@1.60.0-alpha.9
28
+
3
29
  ## 2.5.0
4
30
 
5
31
  ### Minor Changes
@@ -3,7 +3,7 @@ name: mastra-rag
3
3
  description: Documentation for @mastra/rag. Use when working with @mastra/rag APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/rag"
6
- version: "2.5.0"
6
+ version: "2.6.0-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.5.0",
2
+ "version": "2.6.0-alpha.0",
3
3
  "package": "@mastra/rag",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -127,7 +127,7 @@ const { embeddings } = await embedMany({
127
127
  })
128
128
  ```
129
129
 
130
- > **Vector Database Compatibility:** When storing embeddings, the vector database index must be configured to match the output size of your embedding model. If the dimensions don't match, you may get errors or data corruption.
130
+ > **Vector Database Compatibility:** When storing embeddings, configure the vector database index to match the output size of your embedding model to avoid errors or data corruption from mismatched dimensions.
131
131
 
132
132
  ## Example: Complete pipeline
133
133
 
@@ -9,10 +9,7 @@ The `GraphRAG` class implements a graph-based approach to retrieval augmented ge
9
9
  ```typescript
10
10
  import { GraphRAG } from '@mastra/rag'
11
11
 
12
- const graphRag = new GraphRAG({
13
- dimension: 1536,
14
- threshold: 0.7,
15
- })
12
+ const graphRag = new GraphRAG(1536, 0.7)
16
13
 
17
14
  // Create the graph from chunks and embeddings
18
15
  graphRag.createGraph(documentChunks, embeddings)
@@ -88,13 +85,79 @@ Returns an array of `RankedNode` objects, where each node contains:
88
85
 
89
86
  **score** (`number`): Combined relevance score from graph traversal
90
87
 
88
+ ### `serialize`
89
+
90
+ Returns a JSON-safe snapshot of the graph so it can be persisted and restored later instead of rebuilt with `createGraph`.
91
+
92
+ ```typescript
93
+ serialize(): GraphRAGSnapshot
94
+ ```
95
+
96
+ #### Returns
97
+
98
+ Returns a `GraphRAGSnapshot` object containing:
99
+
100
+ **version** (`number`): Snapshot format version, used to reject snapshots this version of the class can't load
101
+
102
+ **dimension** (`number`): Dimension of the embedding vectors the graph was built with
103
+
104
+ **threshold** (`number`): Similarity threshold the graph was built with
105
+
106
+ **nodes** (`GraphNode[]`): All nodes in the graph, each including its full embedding
107
+
108
+ **edges** (`GraphEdge[]`): All edges in the graph
109
+
110
+ The snapshot is a deep copy, so mutating it doesn't affect the graph it came from. Every node carries its full embedding, so snapshots are large: a 1,000-node graph built with 1536-dimension embeddings serializes to about 20 MB of JSON. Size your storage column accordingly.
111
+
112
+ ### `deserialize`
113
+
114
+ Rebuilds a `GraphRAG` instance from a snapshot produced by `serialize`.
115
+
116
+ ```typescript
117
+ static deserialize(snapshot: GraphRAGSnapshot): GraphRAG
118
+ ```
119
+
120
+ #### Parameters
121
+
122
+ **snapshot** (`GraphRAGSnapshot`): A snapshot previously returned by serialize
123
+
124
+ Throws if the snapshot version is unsupported, if a node embedding doesn't match the snapshot dimension, or if an edge references a node that isn't in the snapshot. A bad snapshot therefore fails at load time instead of during a later query.
125
+
126
+ ## Persisting a graph
127
+
128
+ Building a graph is O(n²) in the number of chunks, so rebuilding it on every process start is wasteful. Serialize the graph once and store the snapshot wherever you already keep state. A snapshot is plain JSON, so any store works (a file, a blob column, a key-value cache), and `GraphRAG` doesn't depend on a storage backend.
129
+
130
+ ```typescript
131
+ import { readFile, writeFile } from 'node:fs/promises'
132
+ import { GraphRAG } from '@mastra/rag'
133
+ import type { GraphRAGSnapshot } from '@mastra/rag'
134
+
135
+ const SNAPSHOT_PATH = './docs-graph.json'
136
+
137
+ async function loadOrBuildGraph() {
138
+ try {
139
+ const snapshot = JSON.parse(await readFile(SNAPSHOT_PATH, 'utf8')) as GraphRAGSnapshot
140
+ return GraphRAG.deserialize(snapshot)
141
+ } catch {
142
+ // No usable snapshot yet, so build the graph from scratch
143
+ }
144
+
145
+ const graphRag = new GraphRAG(1536, 0.7)
146
+ graphRag.createGraph(documentChunks, embeddings)
147
+
148
+ await writeFile(SNAPSHOT_PATH, JSON.stringify(graphRag.serialize()))
149
+
150
+ return graphRag
151
+ }
152
+ ```
153
+
154
+ A snapshot reflects the chunks it was built from and isn't updated incrementally. When the underlying documents change, build the graph again and store a new snapshot.
155
+
91
156
  ## Advanced example
92
157
 
93
158
  ```typescript
94
- const graphRag = new GraphRAG({
95
- dimension: 1536,
96
- threshold: 0.8, // Stricter similarity threshold
97
- })
159
+ // Stricter similarity threshold
160
+ const graphRag = new GraphRAG(1536, 0.8)
98
161
 
99
162
  // Create graph from chunks and embeddings
100
163
  graphRag.createGraph(documentChunks, embeddings)
@@ -65,7 +65,7 @@ The tool returns an object with:
65
65
  | ---------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
66
66
  | `content` | `string` | The text content of the retrieved passage. |
67
67
  | `source` | `string \| undefined` | The source URI when Bedrock provides one. Agentic retrieval only includes this field when the result metadata contains `_source_uri`. |
68
- | `score` | `number \| undefined` | The relevance score returned by standard retrieval. The agentic API doesn't return a score for result items. |
68
+ | `score` | `number \| undefined` | The relevance score returned by standard retrieval, which the agentic API omits from result items. |
69
69
  | `metadata` | `Record<string, unknown>` | Additional metadata from the retrieval result. |
70
70
 
71
71
  ## Retrieval Modes
@@ -353,7 +353,7 @@ const response = await agent.generate('Find information about deployment', {
353
353
  })
354
354
  ```
355
355
 
356
- This approach allows you to:
356
+ Runtime configuration supports these use cases:
357
357
 
358
358
  - Switch between environments (dev/staging/prod)
359
359
  - Adjust performance parameters based on load
@@ -29,6 +29,17 @@ export interface GraphChunk {
29
29
  export interface GraphEmbedding {
30
30
  vector: number[];
31
31
  }
32
+ /**
33
+ * JSON-safe snapshot of a GraphRAG instance, produced by `serialize()` and
34
+ * restored with `GraphRAG.deserialize()`.
35
+ */
36
+ export interface GraphRAGSnapshot {
37
+ version: 1;
38
+ dimension: number;
39
+ threshold: number;
40
+ nodes: GraphNode[];
41
+ edges: GraphEdge[];
42
+ }
32
43
  export declare class GraphRAG {
33
44
  private nodes;
34
45
  private edges;
@@ -40,6 +51,23 @@ export declare class GraphRAG {
40
51
  getNodes(): GraphNode[];
41
52
  getEdges(): GraphEdge[];
42
53
  getEdgesByType(type: string): GraphEdge[];
54
+ /**
55
+ * Produce a JSON-safe snapshot of the graph so it can be persisted and
56
+ * restored later instead of rebuilt with `createGraph`.
57
+ *
58
+ * The snapshot is a deep copy, including nested node metadata, so mutating it
59
+ * does not affect this instance.
60
+ * Note that every node carries its full embedding, so snapshots of large
61
+ * graphs can be several megabytes of JSON.
62
+ */
63
+ serialize(): GraphRAGSnapshot;
64
+ /**
65
+ * Rebuild a GraphRAG instance from a snapshot produced by `serialize()`.
66
+ *
67
+ * @throws if the snapshot version is unsupported, a node embedding does not
68
+ * match the snapshot dimension, or an edge references an unknown node.
69
+ */
70
+ static deserialize(snapshot: GraphRAGSnapshot): GraphRAG;
43
71
  clear(): void;
44
72
  updateNodeContent(id: string, newContent: string): void;
45
73
  private getNeighbors;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/graph-rag/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,KAAK,iBAAiB,GAAG,UAAU,CAAC;AACpC,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAGzC,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,UAAW,SAAQ,SAAS;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,GAAE,MAAa,EAAE,SAAS,GAAE,MAAY;IAQ7D,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAW9B,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAe9B,QAAQ,IAAI,SAAS,EAAE;IAKvB,QAAQ,IAAI,SAAS,EAAE;IAIvB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE;IAIzC,KAAK,IAAI,IAAI;IAKb,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IASvD,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,gBAAgB;IAgCxB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE;IAuC9D,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,qBAAqB;IA2C7B;;;;;;;;OAQG;IAEH,KAAK,CAAC,EACJ,KAAK,EACL,IAAS,EACT,eAAqB,EACrB,WAAkB,EAClB,MAAM,GACP,EAAE;QACD,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;KACjC,GAAG,UAAU,EAAE;CA+DjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/graph-rag/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,KAAK,iBAAiB,GAAG,UAAU,CAAC;AACpC,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAGzC,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,UAAW,SAAQ,SAAS;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAID,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,GAAE,MAAa,EAAE,SAAS,GAAE,MAAY;IAQ7D,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAW9B,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAe9B,QAAQ,IAAI,SAAS,EAAE;IAKvB,QAAQ,IAAI,SAAS,EAAE;IAIvB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE;IAIzC;;;;;;;;OAQG;IACH,SAAS,IAAI,gBAAgB;IAc7B;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,QAAQ;IAiCxD,KAAK,IAAI,IAAI;IAKb,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IASvD,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,gBAAgB;IAgCxB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE;IAuC9D,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,qBAAqB;IA2C7B;;;;;;;;OAQG;IAEH,KAAK,CAAC,EACJ,KAAK,EACL,IAAS,EACT,eAAqB,EACrB,WAAkB,EAClB,MAAM,GACP,EAAE;QACD,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;KACjC,GAAG,UAAU,EAAE;CA+DjB"}
package/dist/index.cjs CHANGED
@@ -6224,7 +6224,8 @@ async function rerank(results, query, model, options) {
6224
6224
  }
6225
6225
  //#endregion
6226
6226
  //#region src/graph-rag/index.ts
6227
- var GraphRAG = class {
6227
+ const GRAPH_RAG_SNAPSHOT_VERSION = 1;
6228
+ var GraphRAG = class GraphRAG {
6228
6229
  nodes;
6229
6230
  edges;
6230
6231
  dimension;
@@ -6259,6 +6260,49 @@ var GraphRAG = class {
6259
6260
  getEdgesByType(type) {
6260
6261
  return this.edges.filter((edge) => edge.type === type);
6261
6262
  }
6263
+ /**
6264
+ * Produce a JSON-safe snapshot of the graph so it can be persisted and
6265
+ * restored later instead of rebuilt with `createGraph`.
6266
+ *
6267
+ * The snapshot is a deep copy, including nested node metadata, so mutating it
6268
+ * does not affect this instance.
6269
+ * Note that every node carries its full embedding, so snapshots of large
6270
+ * graphs can be several megabytes of JSON.
6271
+ */
6272
+ serialize() {
6273
+ return {
6274
+ version: GRAPH_RAG_SNAPSHOT_VERSION,
6275
+ dimension: this.dimension,
6276
+ threshold: this.threshold,
6277
+ nodes: Array.from(this.nodes.values()).map((node) => ({
6278
+ ...node,
6279
+ ...node.embedding ? { embedding: [...node.embedding] } : {},
6280
+ ...node.metadata ? { metadata: structuredClone(node.metadata) } : {}
6281
+ })),
6282
+ edges: this.edges.map((edge) => ({ ...edge }))
6283
+ };
6284
+ }
6285
+ /**
6286
+ * Rebuild a GraphRAG instance from a snapshot produced by `serialize()`.
6287
+ *
6288
+ * @throws if the snapshot version is unsupported, a node embedding does not
6289
+ * match the snapshot dimension, or an edge references an unknown node.
6290
+ */
6291
+ static deserialize(snapshot) {
6292
+ if (snapshot?.version !== GRAPH_RAG_SNAPSHOT_VERSION) throw new Error(`Unsupported GraphRAG snapshot version: ${snapshot?.version}`);
6293
+ const graph = new GraphRAG(snapshot.dimension, snapshot.threshold);
6294
+ for (const node of snapshot.nodes ?? []) graph.addNode({
6295
+ ...node,
6296
+ ...node.embedding ? { embedding: [...node.embedding] } : {},
6297
+ ...node.metadata ? { metadata: structuredClone(node.metadata) } : {}
6298
+ });
6299
+ for (const edge of snapshot.edges ?? []) {
6300
+ if (!graph.nodes.has(edge.source)) throw new Error(`Edge references unknown node: ${edge.source}`);
6301
+ if (!graph.nodes.has(edge.target)) throw new Error(`Edge references unknown node: ${edge.target}`);
6302
+ }
6303
+ graph.edges = (snapshot.edges ?? []).map((edge) => ({ ...edge }));
6304
+ return graph;
6305
+ }
6262
6306
  clear() {
6263
6307
  this.nodes.clear();
6264
6308
  this.edges = [];