memo-grafter 0.2.0 → 0.2.2
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/README.md +73 -187
- package/USER_GUIDE.md +158 -40
- package/dist/MemoGrafter.d.ts +2 -0
- package/dist/MemoGrafter.d.ts.map +1 -1
- package/dist/MemoGrafter.js +15 -0
- package/dist/MemoGrafter.js.map +1 -1
- package/dist/MemoGrafterAgent.d.ts +4 -1
- package/dist/MemoGrafterAgent.d.ts.map +1 -1
- package/dist/MemoGrafterAgent.js +42 -12
- package/dist/MemoGrafterAgent.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/GrafterPipeline.d.ts +7 -1
- package/dist/pipeline/GrafterPipeline.d.ts.map +1 -1
- package/dist/pipeline/GrafterPipeline.js +5 -1
- package/dist/pipeline/GrafterPipeline.js.map +1 -1
- package/dist/pipeline/IngestPipeline.d.ts +12 -1
- package/dist/pipeline/IngestPipeline.d.ts.map +1 -1
- package/dist/pipeline/IngestPipeline.js +9 -1
- package/dist/pipeline/IngestPipeline.js.map +1 -1
- package/dist/pipeline/RetrieverPipeline.d.ts +14 -1
- package/dist/pipeline/RetrieverPipeline.d.ts.map +1 -1
- package/dist/pipeline/RetrieverPipeline.js +35 -2
- package/dist/pipeline/RetrieverPipeline.js.map +1 -1
- package/dist/store/GraphStore.d.ts +2 -0
- package/dist/store/GraphStore.d.ts.map +1 -1
- package/dist/store/postgres-pgvector/GraphStore.d.ts +2 -0
- package/dist/store/postgres-pgvector/GraphStore.d.ts.map +1 -1
- package/dist/store/postgres-pgvector/GraphStore.js +26 -0
- package/dist/store/postgres-pgvector/GraphStore.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/drift/driftScore.d.ts.map +1 -1
- package/dist/utils/drift/driftScore.js +2 -1
- package/dist/utils/drift/driftScore.js.map +1 -1
- package/dist/utils/reentry/reentryText.d.ts.map +1 -1
- package/dist/utils/reentry/reentryText.js +2 -2
- package/dist/utils/reentry/reentryText.js.map +1 -1
- package/dist/utils/text/tokenCount.d.ts +1 -0
- package/dist/utils/text/tokenCount.d.ts.map +1 -1
- package/dist/utils/text/tokenCount.js +3 -0
- package/dist/utils/text/tokenCount.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,187 +1,73 @@
|
|
|
1
|
-
# MemoGrafter
|
|
2
|
-
[](https://www.npmjs.com/package/memo-grafter)
|
|
3
|
-
|
|
4
|
-
Structured memory for TypeScript chatbots.
|
|
5
|
-
|
|
6
|
-
MemoGrafter helps chatbot applications remember conversations without stuffing every old message back into the prompt. It turns
|
|
7
|
-
|
|
8
|
-
It is a memory framework, not an autonomous agent runtime. It does not run tools, schedule
|
|
9
|
-
|
|
10
|
-
## What
|
|
11
|
-
|
|
12
|
-
- Chatbots that
|
|
13
|
-
-
|
|
14
|
-
- Multi-chatbot
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
->
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
console.log(recall.facts);
|
|
77
|
-
console.log(recall.systemPrompt);
|
|
78
|
-
|
|
79
|
-
await agent.close();
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Adapters
|
|
83
|
-
|
|
84
|
-
MemoGrafter includes provider adapters such as `OpenAILLMAdapter`, `OpenAIEmbedAdapter`, `AnthropicLLMAdapter`, `GeminiLLMAdapter`, and `GeminiEmbedAdapter`. You can also bring any provider by implementing the public adapter interfaces:
|
|
85
|
-
|
|
86
|
-
```ts
|
|
87
|
-
import {
|
|
88
|
-
type EmbedAdapter,
|
|
89
|
-
type LLMAdapter,
|
|
90
|
-
type Message,
|
|
91
|
-
} from "memo-grafter";
|
|
92
|
-
|
|
93
|
-
class MyLLMAdapter implements LLMAdapter {
|
|
94
|
-
async complete(messages: Message[], system?: string): Promise<string> {
|
|
95
|
-
// Call your model provider here.
|
|
96
|
-
return "Assistant response";
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
class MyEmbedAdapter implements EmbedAdapter {
|
|
101
|
-
async embed(text: string): Promise<number[]> {
|
|
102
|
-
// Return an embedding vector matching your storage schema.
|
|
103
|
-
return [];
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Storage
|
|
109
|
-
|
|
110
|
-
MemoGrafter uses a public `GraphStore` interface internally. The default implementation is `PostgresGraphStore`, backed by PostgreSQL and `pgvector`, and this is what `MemoGrafter` and `MemoGrafterAgent` construct from the `db.connectionString` config today.
|
|
111
|
-
|
|
112
|
-
```ts
|
|
113
|
-
import {
|
|
114
|
-
PostgresGraphStore,
|
|
115
|
-
type GraphStore,
|
|
116
|
-
} from "memo-grafter";
|
|
117
|
-
|
|
118
|
-
const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
The interface boundary keeps the Postgres implementation isolated and gives future storage backends a clear contract to implement.
|
|
122
|
-
|
|
123
|
-
## Targeted Recall
|
|
124
|
-
|
|
125
|
-
Use `agent.recall()` when you want structured memory back without asking the LLM to answer yet. It searches atomic memory nodes by meaning, filters stale memories, groups them with their parent topic summaries, and returns a prompt block you can inspect or pass to your own model call.
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
const result = await agent.recall("deployment config", {
|
|
129
|
-
limit: 8,
|
|
130
|
-
minSimilarity: 0.55,
|
|
131
|
-
tokenBudget: 1000,
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
console.log(result.facts);
|
|
135
|
-
console.log(result.nodes);
|
|
136
|
-
console.log(result.systemPrompt);
|
|
137
|
-
console.log(result.tokenCount);
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
`recall()` is side-effect free. It does not call `invoke()`, does not mutate chat history, and does not inject anything automatically.
|
|
141
|
-
|
|
142
|
-
## Memory Grafting
|
|
143
|
-
|
|
144
|
-
Memory grafting is the core idea behind the name: one chatbot can build memory, and another chatbot can absorb only the useful parts.
|
|
145
|
-
|
|
146
|
-
```ts
|
|
147
|
-
await writingBot.absorbFromAgent(travelBot, {
|
|
148
|
-
prompt: "Japan travel preferences",
|
|
149
|
-
limit: 3,
|
|
150
|
-
});
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
## Drift And Reentry
|
|
154
|
-
|
|
155
|
-
Use `driftSensitivity` for developer-friendly topic segmentation:
|
|
156
|
-
|
|
157
|
-
```ts
|
|
158
|
-
const agent = new MemoGrafterAgent({
|
|
159
|
-
db: { connectionString: process.env.DATABASE_URL! },
|
|
160
|
-
llm,
|
|
161
|
-
embedder,
|
|
162
|
-
drift: {
|
|
163
|
-
mode: "intent",
|
|
164
|
-
driftSensitivity: "medium",
|
|
165
|
-
minSegmentMessages: 3,
|
|
166
|
-
reentryDetection: true,
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Sensitivity presets are `"low"`, `"medium"`, and `"high"`. The older numeric `threshold` option is still accepted for compatibility, but `driftSensitivity` is preferred.
|
|
172
|
-
|
|
173
|
-
When a conversation returns to an earlier topic, MemoGrafter can create a `reentry` edge between the new topic node and the earlier related topic node. For example, a chat can move from database decisions to authentication, then back to database pooling; the later database segment is linked back to the original database topic instead of becoming an isolated duplicate.
|
|
174
|
-
|
|
175
|
-
## Learn More
|
|
176
|
-
|
|
177
|
-
Read [USER_GUIDE.md](https://github.com/mayhemking007/memo-grafter/blob/main/USER_GUIDE.md) for setup, configuration, queue mode, custom adapters, fleet APIs, examples, and troubleshooting.
|
|
178
|
-
|
|
179
|
-
This repository also includes a runnable demo:
|
|
180
|
-
|
|
181
|
-
```text
|
|
182
|
-
examples/chatbot-memory-demo
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## License
|
|
186
|
-
|
|
187
|
-
MIT
|
|
1
|
+
# MemoGrafter
|
|
2
|
+
[](https://www.npmjs.com/package/memo-grafter)
|
|
3
|
+
|
|
4
|
+
Structured memory for TypeScript chatbots.
|
|
5
|
+
|
|
6
|
+
MemoGrafter helps chatbot applications remember conversations without stuffing every old message back into the prompt. It turns conversation history into topic-based memory, recalls relevant details later, and can copy useful memory from one chatbot or session into another.
|
|
7
|
+
|
|
8
|
+
It is a memory framework, not an autonomous agent runtime. It does not run tools, schedule work, or decide goals for an agent.
|
|
9
|
+
|
|
10
|
+
## What It Is For
|
|
11
|
+
|
|
12
|
+
- Chatbots that need long-running memory.
|
|
13
|
+
- Assistants that should recall user preferences, prior context, and open questions.
|
|
14
|
+
- Multi-chatbot or multi-session flows where selected memory can be grafted into another conversation.
|
|
15
|
+
- TypeScript apps that need reusable memory, retrieval, and graph-backed conversation primitives.
|
|
16
|
+
|
|
17
|
+
## How It Works
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
chat messages
|
|
21
|
+
-> topic-based memory
|
|
22
|
+
-> graph links
|
|
23
|
+
-> relevant recall
|
|
24
|
+
-> optional memory grafting
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
MemoGrafter stores conversation turns, detects topic changes, summarizes useful context, links related memories, and retrieves or grafts memory when needed.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install memo-grafter
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
MemoGrafter runs server-side on Node.js. The built-in storage backend uses PostgreSQL with `pgvector`.
|
|
36
|
+
|
|
37
|
+
## Minimal Example
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import "dotenv/config";
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
MemoGrafterAgent,
|
|
44
|
+
OpenAIEmbedAdapter,
|
|
45
|
+
OpenAILLMAdapter,
|
|
46
|
+
} from "memo-grafter";
|
|
47
|
+
|
|
48
|
+
const agent = new MemoGrafterAgent({
|
|
49
|
+
db: { connectionString: process.env.DATABASE_URL! },
|
|
50
|
+
llm: new OpenAILLMAdapter("gpt-4o"),
|
|
51
|
+
embedder: new OpenAIEmbedAdapter("text-embedding-3-small"),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
await agent.initialize();
|
|
55
|
+
|
|
56
|
+
await agent.invoke("I am planning a Japan trip.");
|
|
57
|
+
await agent.invoke("I like quiet towns, bookstores, and local cafes.");
|
|
58
|
+
|
|
59
|
+
const recall = await agent.recall("travel preferences");
|
|
60
|
+
console.log(recall.facts);
|
|
61
|
+
|
|
62
|
+
await agent.close();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Learn More
|
|
66
|
+
|
|
67
|
+
- [USER_GUIDE.md](https://github.com/mayhemking007/memo-grafter/blob/main/USER_GUIDE.md) covers setup, configuration, adapters, queue mode, fleet APIs, examples, and troubleshooting.
|
|
68
|
+
- [ARCHITECTURE.md](https://github.com/mayhemking007/memo-grafter/blob/main/ARCHITECTURE.md) explains the current high-level implementation.
|
|
69
|
+
- `examples` contains runnable demo.
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/USER_GUIDE.md
CHANGED
|
@@ -18,7 +18,7 @@ The most important idea is memory grafting. A chatbot can build useful memory du
|
|
|
18
18
|
- An OpenAI API key only if using the included OpenAI adapters.
|
|
19
19
|
- An Anthropic API key only if using the included Anthropic LLM adapter.
|
|
20
20
|
- A Gemini API key only if using the included Gemini adapters.
|
|
21
|
-
- Redis only if enabling queue mode.
|
|
21
|
+
- Redis only if enabling queue mode or the optional recall cache.
|
|
22
22
|
|
|
23
23
|
MemoGrafter is server-side only. Do not run it in browser code.
|
|
24
24
|
|
|
@@ -65,7 +65,7 @@ REDIS_URL=redis://localhost:6379
|
|
|
65
65
|
|
|
66
66
|
`GEMINI_API_KEY` is required only when using `GeminiLLMAdapter` or `GeminiEmbedAdapter`.
|
|
67
67
|
|
|
68
|
-
`REDIS_URL` is optional and only needed when you pass `queue` config.
|
|
68
|
+
`REDIS_URL` is optional and only needed when you pass `queue` or `cache` config.
|
|
69
69
|
|
|
70
70
|
Enable `pgvector` in PostgreSQL:
|
|
71
71
|
|
|
@@ -135,12 +135,12 @@ A message is one user or assistant turn:
|
|
|
135
135
|
|
|
136
136
|
```ts
|
|
137
137
|
export interface Message {
|
|
138
|
-
role: "user" | "assistant";
|
|
138
|
+
role: "system" | "user" | "assistant";
|
|
139
139
|
content: string;
|
|
140
140
|
}
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
`MemoGrafterAgent` keeps an in-memory history for the current session and stores messages in PostgreSQL during ingestion.
|
|
143
|
+
`MemoGrafterAgent` keeps an in-memory user/assistant history for the current session and stores messages in PostgreSQL during ingestion. System messages can be added to LLM calls internally when memory recall is pinned into an overflowing context window.
|
|
144
144
|
|
|
145
145
|
### Segments
|
|
146
146
|
|
|
@@ -252,13 +252,15 @@ await agent.close();
|
|
|
252
252
|
On every call, `invoke()`:
|
|
253
253
|
|
|
254
254
|
1. Adds the user message to local history.
|
|
255
|
-
2.
|
|
256
|
-
3.
|
|
257
|
-
4.
|
|
255
|
+
2. Builds the message list for the LLM.
|
|
256
|
+
3. If history is under the configured budget, sends the raw local history.
|
|
257
|
+
4. If history crosses the overflow threshold, calls targeted recall using recent conversation context, prepends the recall result as one pinned system message, and keeps only the recent raw message window.
|
|
258
258
|
5. Adds the assistant response to history.
|
|
259
|
-
6.
|
|
259
|
+
6. Queues ingestion of the updated conversation into the memory graph.
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
The overflow threshold is 80% of `inject.tokenBudget`. Recall failures are logged as warnings and fall back to the recent raw message window, so a retrieval or embedder problem should not crash the foreground chatbot turn.
|
|
262
|
+
|
|
263
|
+
On the first turn there may be no memory to recall. Later turns can use memory created from earlier turns once ingestion has completed.
|
|
262
264
|
|
|
263
265
|
### Targeted Recall
|
|
264
266
|
|
|
@@ -269,6 +271,9 @@ const result = await agent.recall("deployment config", {
|
|
|
269
271
|
limit: 8,
|
|
270
272
|
minSimilarity: 0.55,
|
|
271
273
|
tokenBudget: 1000,
|
|
274
|
+
cache: {
|
|
275
|
+
ttlSeconds: 90,
|
|
276
|
+
},
|
|
272
277
|
});
|
|
273
278
|
|
|
274
279
|
console.log(result.facts);
|
|
@@ -289,17 +294,42 @@ Options:
|
|
|
289
294
|
- `limit`: max memory nodes to fetch before filtering. Defaults to `10`.
|
|
290
295
|
- `minSimilarity`: cosine similarity floor. Defaults to `0.6`.
|
|
291
296
|
- `tokenBudget`: max approximate tokens for included fact blocks. Defaults to `1200`.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
297
|
+
- `cache.ttlSeconds`: per-call recall cache TTL override when `MemoGrafterConfig.cache` is enabled. Values are clamped to 60-120 seconds.
|
|
298
|
+
|
|
299
|
+
`recall()` is side-effect free. It does not call `invoke()`, does not trigger a new LLM completion, and does not mutate local history. Your application can call it directly to display memories, add `result.systemPrompt` to a model call, or ignore the result.
|
|
300
|
+
|
|
301
|
+
`MemoGrafterAgent.invoke()` also calls `recall()` internally when local history overflows the configured history budget. In that automatic path, the returned `systemPrompt` is pinned as a single system message before the recent raw chat window.
|
|
302
|
+
|
|
303
|
+
If you call `recall()` immediately after `invoke()`, it only sees memory that has already been ingested into storage. In queue mode, wait for your background worker to finish before expecting newly created memories to appear.
|
|
304
|
+
|
|
305
|
+
## Inspecting Memory
|
|
306
|
+
|
|
307
|
+
Read a complete session graph snapshot:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const snapshot = await agent.getGraphSnapshot();
|
|
311
|
+
|
|
312
|
+
console.log(snapshot.sessionId);
|
|
313
|
+
console.log(snapshot.nodes);
|
|
314
|
+
console.log(snapshot.edges);
|
|
315
|
+
console.log(snapshot.memories);
|
|
316
|
+
console.log(snapshot.capturedAt);
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
`getGraphSnapshot()` returns a `GraphSnapshot`:
|
|
320
|
+
|
|
321
|
+
- `sessionId`: current agent session ID.
|
|
322
|
+
- `nodes`: active topic nodes for the session.
|
|
323
|
+
- `edges`: topic edges where either endpoint belongs to a session topic node.
|
|
324
|
+
- `memories`: all memory nodes for the session, including decayed or superseded rows.
|
|
325
|
+
- `capturedAt`: ISO timestamp for when the snapshot was produced.
|
|
326
|
+
|
|
327
|
+
This method is read-only. It does not include raw `mg_message_buffer` content and does not add rendering, layout, or color decisions. Like `getActiveNodes()` and `getActiveSegments()`, it waits for the agent's pending ingest work before reading. If called immediately after `invoke()` in queue mode, it waits for the current ingest job to settle before returning.
|
|
328
|
+
|
|
329
|
+
Read active topic nodes:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
const nodes = await agent.getActiveNodes();
|
|
303
333
|
|
|
304
334
|
for (const node of nodes) {
|
|
305
335
|
console.log({
|
|
@@ -449,6 +479,11 @@ const agent = new MemoGrafterAgent({
|
|
|
449
479
|
inject: {
|
|
450
480
|
bufferSize: 4,
|
|
451
481
|
tokenBudget: 1500,
|
|
482
|
+
recentWindowSize: 20,
|
|
483
|
+
},
|
|
484
|
+
cache: {
|
|
485
|
+
connectionString: process.env.REDIS_URL!,
|
|
486
|
+
ttlSeconds: 90,
|
|
452
487
|
},
|
|
453
488
|
});
|
|
454
489
|
```
|
|
@@ -476,12 +511,14 @@ const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
|
|
|
476
511
|
|
|
477
512
|
`GraphStore` is the public storage interface. `PostgresGraphStore` is the default PostgreSQL and pgvector implementation.
|
|
478
513
|
|
|
479
|
-
Useful store inspection methods include:
|
|
480
|
-
|
|
481
|
-
- `getNodesBySession(sessionId)`: read topic nodes for a session.
|
|
482
|
-
- `getTopicNode(topicNodeId, sessionId?)`: read one topic node by ID.
|
|
483
|
-
- `getSegmentsBySession(sessionId)`: read topic segments for a session.
|
|
484
|
-
- `getEdgesByType(sessionId, type)`: inspect graph edges such as `"reentry"`, `"semantic"`, `"temporal"`, or `"grafted"`.
|
|
514
|
+
Useful store inspection methods include:
|
|
515
|
+
|
|
516
|
+
- `getNodesBySession(sessionId)`: read topic nodes for a session.
|
|
517
|
+
- `getTopicNode(topicNodeId, sessionId?)`: read one topic node by ID.
|
|
518
|
+
- `getSegmentsBySession(sessionId)`: read topic segments for a session.
|
|
519
|
+
- `getEdgesByType(sessionId, type)`: inspect graph edges such as `"reentry"`, `"semantic"`, `"temporal"`, or `"grafted"`.
|
|
520
|
+
- `getEdgesBySession(sessionId)`: read all topic edges where either endpoint belongs to the session's topic nodes.
|
|
521
|
+
- `getMemoriesBySession(sessionId)`: read all memory nodes for a session, including decayed and superseded rows.
|
|
485
522
|
|
|
486
523
|
### `llm`
|
|
487
524
|
|
|
@@ -592,13 +629,33 @@ Controls graph retrieval and traversal.
|
|
|
592
629
|
inject: {
|
|
593
630
|
bufferSize: 4,
|
|
594
631
|
tokenBudget: 1500,
|
|
632
|
+
recentWindowSize: 20,
|
|
595
633
|
}
|
|
596
634
|
```
|
|
597
635
|
|
|
598
|
-
Controls
|
|
636
|
+
Controls memory prompt sizing and the raw history window kept when chat history overflows.
|
|
599
637
|
|
|
600
638
|
- `bufferSize`: nearby raw messages to include.
|
|
601
|
-
- `tokenBudget`: approximate memory
|
|
639
|
+
- `tokenBudget`: approximate token budget used for memory prompts and agent history overflow checks. `MemoGrafterAgent` starts overflow handling at 80% of this value.
|
|
640
|
+
- `recentWindowSize`: number of newest raw chat messages to keep after the pinned recall block during overflow. Defaults to `20`.
|
|
641
|
+
|
|
642
|
+
When `MemoGrafterAgent` detects overflow, it builds a recall query from the last six message contents, calls recall with `{ limit: 5, minSimilarity: 0.65 }`, prepends the returned memory prompt as a system message, and keeps the last `recentWindowSize` raw messages. If recall fails, it uses only that recent raw window.
|
|
643
|
+
|
|
644
|
+
### `cache`
|
|
645
|
+
|
|
646
|
+
```ts
|
|
647
|
+
cache: {
|
|
648
|
+
connectionString: process.env.REDIS_URL!,
|
|
649
|
+
ttlSeconds: 90,
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
Enables an opt-in Redis cache for targeted recall. MemoGrafter creates one shared Redis client and uses it to cache only the raw `searchMemories()` result. It does not cache final prompts, filtered blocks, or `RetrievalResult`, so different `tokenBudget` values still assemble fresh output.
|
|
654
|
+
|
|
655
|
+
- `connectionString`: Redis URL.
|
|
656
|
+
- `ttlSeconds`: cache TTL in seconds. Defaults to `90` and is clamped between `60` and `120`.
|
|
657
|
+
|
|
658
|
+
Recall cache keys include the session ID, `limit`, `minSimilarity`, and a deterministic hash of the query embedding. Redis failures are logged as warnings and recall falls back to PostgreSQL search. The cache is disabled unless this section is present.
|
|
602
659
|
|
|
603
660
|
## Queue Mode
|
|
604
661
|
|
|
@@ -695,6 +752,60 @@ class MyGraphStore implements GraphStore {
|
|
|
695
752
|
|
|
696
753
|
Future storage implementations can use the same interface without changing pipeline or fleet code. For example, a SQLite plus vector-database implementation would implement `GraphStore` while preserving the same behavior expected by ingestion, grafting, and fleet APIs.
|
|
697
754
|
|
|
755
|
+
## Using Pipelines Directly
|
|
756
|
+
|
|
757
|
+
`MemoGrafterAgent` is the recommended starting point, but the underlying
|
|
758
|
+
pipeline classes are also exported for developers who want to build custom
|
|
759
|
+
agent loops or integrate MemoGrafter memory primitives into an existing
|
|
760
|
+
orchestration framework.
|
|
761
|
+
|
|
762
|
+
Pipeline classes are exported for composability. Their constructors and
|
|
763
|
+
internal behavior are not covered by semver stability guarantees until v1.0.
|
|
764
|
+
Breaking changes to pipeline internals may occur in minor versions.
|
|
765
|
+
|
|
766
|
+
Available pipeline exports:
|
|
767
|
+
|
|
768
|
+
- `IngestPipeline`: segments messages, builds topic nodes, extracts memory nodes, and writes graph edges.
|
|
769
|
+
- `RetrieverPipeline`: embeds a query, searches memory nodes, and returns a structured `RetrievalResult`.
|
|
770
|
+
- `GrafterPipeline`: traverses the topic graph and assembles a token-budget-fitted system prompt.
|
|
771
|
+
|
|
772
|
+
Example using `RetrieverPipeline` directly:
|
|
773
|
+
|
|
774
|
+
```ts
|
|
775
|
+
import {
|
|
776
|
+
PostgresGraphStore,
|
|
777
|
+
RetrieverPipeline,
|
|
778
|
+
OpenAIEmbedAdapter,
|
|
779
|
+
} from "memo-grafter";
|
|
780
|
+
|
|
781
|
+
const store = new PostgresGraphStore(process.env.DATABASE_URL!);
|
|
782
|
+
await store.initialize();
|
|
783
|
+
|
|
784
|
+
const embedder = new OpenAIEmbedAdapter("text-embedding-3-small");
|
|
785
|
+
|
|
786
|
+
const retriever = new RetrieverPipeline(store, embedder, {
|
|
787
|
+
limit: 8,
|
|
788
|
+
minSimilarity: 0.55,
|
|
789
|
+
tokenBudget: 1000,
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
const result = await retriever.run(
|
|
793
|
+
"deployment config and Kubernetes namespace",
|
|
794
|
+
sessionId,
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
console.log(result.facts);
|
|
798
|
+
console.log(result.systemPrompt);
|
|
799
|
+
|
|
800
|
+
await store.close();
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
When using pipelines directly you are responsible for managing the store
|
|
804
|
+
connection lifecycle. Call `store.close()` during graceful shutdown.
|
|
805
|
+
|
|
806
|
+
`MemoGrafterAgent` remains the batteries-included default. Existing code
|
|
807
|
+
that uses `MemoGrafterAgent` does not need to change.
|
|
808
|
+
|
|
698
809
|
## Fleet API
|
|
699
810
|
|
|
700
811
|
Fleets let you group color-scoped worker chatbots and use a conductor to graft memory across workers.
|
|
@@ -868,7 +979,7 @@ const result = await agent.recall("Japan travel preferences", {
|
|
|
868
979
|
|
|
869
980
|
### Redis Warnings
|
|
870
981
|
|
|
871
|
-
Redis is only required when you pass `queue` config. If you do not need background ingestion, remove
|
|
982
|
+
Redis is only required when you pass `queue` or `cache` config. If you do not need background ingestion or recall caching, remove those sections.
|
|
872
983
|
|
|
873
984
|
### Browser Runtime Error
|
|
874
985
|
|
|
@@ -884,6 +995,7 @@ Practical notes:
|
|
|
884
995
|
- Use PostgreSQL with `pgvector` enabled.
|
|
885
996
|
- Tune `tokenBudget` to control prompt size and cost.
|
|
886
997
|
- Use queue mode if ingestion becomes slow.
|
|
998
|
+
- Use the optional recall cache for long sessions with repeated or automatic overflow recall.
|
|
887
999
|
- Store your own user/session mapping outside MemoGrafter.
|
|
888
1000
|
- Call `close()` during graceful shutdown.
|
|
889
1001
|
- Do not expose database credentials or OpenAI keys to browser code.
|
|
@@ -904,27 +1016,33 @@ Main exports:
|
|
|
904
1016
|
- `OpenAILLMAdapter`
|
|
905
1017
|
- `OpenAIEmbedAdapter`
|
|
906
1018
|
- `PostgresGraphStore`
|
|
1019
|
+
- `GrafterPipeline`
|
|
1020
|
+
- `IngestPipeline`
|
|
1021
|
+
- `RetrieverPipeline`
|
|
907
1022
|
- `GraphStore`
|
|
908
1023
|
- `FleetAgentRecord`
|
|
909
1024
|
- `RetrievalResult`
|
|
910
1025
|
- `RetrieverConfig`
|
|
911
1026
|
- public shared and fleet types
|
|
912
1027
|
|
|
913
|
-
Useful `GraphStore` inspection methods:
|
|
914
|
-
|
|
915
|
-
- `getTopicNode(topicNodeId, sessionId?)`
|
|
916
|
-
- `getNodesBySession(sessionId)`
|
|
917
|
-
- `getSegmentsBySession(sessionId)`
|
|
918
|
-
- `getEdgesByType(sessionId, type)`
|
|
919
|
-
|
|
920
|
-
|
|
1028
|
+
Useful `GraphStore` inspection methods:
|
|
1029
|
+
|
|
1030
|
+
- `getTopicNode(topicNodeId, sessionId?)`
|
|
1031
|
+
- `getNodesBySession(sessionId)`
|
|
1032
|
+
- `getSegmentsBySession(sessionId)`
|
|
1033
|
+
- `getEdgesByType(sessionId, type)`
|
|
1034
|
+
- `getEdgesBySession(sessionId)`
|
|
1035
|
+
- `getMemoriesBySession(sessionId)`
|
|
1036
|
+
|
|
1037
|
+
Common `MemoGrafterAgent` methods:
|
|
921
1038
|
|
|
922
1039
|
- `initialize()`: initialize storage.
|
|
923
1040
|
- `invoke(message)`: send a user message and receive an assistant response.
|
|
924
1041
|
- `getHistory()`: read local chat history.
|
|
925
|
-
- `getSessionId()`: read the current session ID.
|
|
926
|
-
- `
|
|
927
|
-
- `
|
|
1042
|
+
- `getSessionId()`: read the current session ID.
|
|
1043
|
+
- `getGraphSnapshot()`: read nodes, edges, memories, session ID, and capture timestamp for visualization or inspection.
|
|
1044
|
+
- `getActiveNodes()`: inspect topic nodes.
|
|
1045
|
+
- `getActiveSegments()`: inspect topic segments.
|
|
928
1046
|
- `recall(query, options?)`: retrieve structured memory by semantic query.
|
|
929
1047
|
- `graft(topicIds?)`: preview memory injection.
|
|
930
1048
|
- `ingestGraftedNodes(nodes)`: copy provided nodes into this agent.
|
package/dist/MemoGrafter.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Redis } from "ioredis";
|
|
1
2
|
import { MemoGrafterFleet } from "./fleet/MemoGrafterFleet.js";
|
|
2
3
|
import type { MemoGrafterFleetOptions } from "./fleet/types.js";
|
|
3
4
|
import type { GraphStore } from "./store/index.js";
|
|
@@ -6,6 +7,7 @@ export declare class MemoGrafter {
|
|
|
6
7
|
readonly llm: LLMAdapter;
|
|
7
8
|
readonly embedder: EmbedAdapter;
|
|
8
9
|
readonly store: GraphStore;
|
|
10
|
+
readonly recallCache: Redis | null;
|
|
9
11
|
private readonly ingestPipeline;
|
|
10
12
|
private readonly grafterPipeline;
|
|
11
13
|
private readonly ingestQueue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoGrafter.d.ts","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MemoGrafter.d.ts","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAKhC,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EACV,sBAAsB,EACtB,YAAY,EACZ,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,SAAS,EACT,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,qBAAa,WAAW;IACtB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAErC,MAAM,EAAE,iBAAiB;IAmDrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQpE,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAIjE,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASpE,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IAM7F,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAIjE,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMrF,oBAAoB,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAmBpG,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMpF,WAAW,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB;IAI9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,CAAC,uBAAuB;CAUhC"}
|
package/dist/MemoGrafter.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Redis } from "ioredis";
|
|
1
2
|
import { GrafterPipeline } from "./pipeline/GrafterPipeline.js";
|
|
2
3
|
import { IngestPipeline } from "./pipeline/IngestPipeline.js";
|
|
3
4
|
import { IngestQueue } from "./queue/IngestQueue.js";
|
|
@@ -7,6 +8,7 @@ export class MemoGrafter {
|
|
|
7
8
|
llm;
|
|
8
9
|
embedder;
|
|
9
10
|
store;
|
|
11
|
+
recallCache;
|
|
10
12
|
ingestPipeline;
|
|
11
13
|
grafterPipeline;
|
|
12
14
|
ingestQueue;
|
|
@@ -27,6 +29,15 @@ export class MemoGrafter {
|
|
|
27
29
|
this.llm = config.llm;
|
|
28
30
|
this.embedder = config.embedder;
|
|
29
31
|
this.store = new PostgresGraphStore(config.db.connectionString);
|
|
32
|
+
this.recallCache = config.cache
|
|
33
|
+
? new Redis(config.cache.connectionString, {
|
|
34
|
+
enableOfflineQueue: false,
|
|
35
|
+
maxRetriesPerRequest: 1,
|
|
36
|
+
})
|
|
37
|
+
: null;
|
|
38
|
+
this.recallCache?.on("error", (error) => {
|
|
39
|
+
console.warn("MemoGrafter recall cache Redis warning:", error.message);
|
|
40
|
+
});
|
|
30
41
|
const ingestConfig = {
|
|
31
42
|
windowSize,
|
|
32
43
|
topK,
|
|
@@ -105,6 +116,10 @@ export class MemoGrafter {
|
|
|
105
116
|
}
|
|
106
117
|
async close() {
|
|
107
118
|
await this.ingestQueue?.close();
|
|
119
|
+
await this.recallCache?.quit().catch((error) => {
|
|
120
|
+
console.warn("MemoGrafter recall cache close warning:", error);
|
|
121
|
+
this.recallCache?.disconnect();
|
|
122
|
+
});
|
|
108
123
|
await this.store.close();
|
|
109
124
|
}
|
|
110
125
|
assertServerEnvironment() {
|
package/dist/MemoGrafter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoGrafter.js","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAc/D,MAAM,OAAO,WAAW;IACb,GAAG,CAAa;IAChB,QAAQ,CAAe;IACvB,KAAK,CAAa;
|
|
1
|
+
{"version":3,"file":"MemoGrafter.js","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"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"}
|