memo-grafter 0.1.1 → 0.2.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/.env.example +1 -0
- package/README.md +187 -95
- package/USER_GUIDE.md +226 -9
- package/dist/MemoGrafter.d.ts +1 -1
- package/dist/MemoGrafter.d.ts.map +1 -1
- package/dist/MemoGrafter.js +16 -5
- package/dist/MemoGrafter.js.map +1 -1
- package/dist/MemoGrafterAgent.d.ts +7 -1
- package/dist/MemoGrafterAgent.d.ts.map +1 -1
- package/dist/MemoGrafterAgent.js +45 -8
- package/dist/MemoGrafterAgent.js.map +1 -1
- package/dist/adapters/AnthropicAdapter.d.ts.map +1 -1
- package/dist/adapters/AnthropicAdapter.js +8 -2
- package/dist/adapters/AnthropicAdapter.js.map +1 -1
- package/dist/adapters/GeminiAdapter.d.ts +15 -0
- package/dist/adapters/GeminiAdapter.d.ts.map +1 -0
- package/dist/adapters/GeminiAdapter.js +48 -0
- package/dist/adapters/GeminiAdapter.js.map +1 -0
- package/dist/fleet/FleetStore.d.ts +1 -1
- package/dist/fleet/FleetStore.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/GrafterPipeline.d.ts +1 -2
- package/dist/pipeline/GrafterPipeline.d.ts.map +1 -1
- package/dist/pipeline/GrafterPipeline.js +3 -5
- package/dist/pipeline/GrafterPipeline.js.map +1 -1
- package/dist/pipeline/IngestPipeline.d.ts +7 -3
- package/dist/pipeline/IngestPipeline.d.ts.map +1 -1
- package/dist/pipeline/IngestPipeline.js +31 -5
- package/dist/pipeline/IngestPipeline.js.map +1 -1
- package/dist/pipeline/RetrieverPipeline.d.ts +11 -0
- package/dist/pipeline/RetrieverPipeline.d.ts.map +1 -0
- package/dist/pipeline/RetrieverPipeline.js +73 -0
- package/dist/pipeline/RetrieverPipeline.js.map +1 -0
- package/dist/pipeline/SegmentProcessor.d.ts +3 -2
- package/dist/pipeline/SegmentProcessor.d.ts.map +1 -1
- package/dist/pipeline/SegmentProcessor.js +50 -18
- package/dist/pipeline/SegmentProcessor.js.map +1 -1
- package/dist/pipeline/TopicDriftDetector.d.ts +21 -3
- package/dist/pipeline/TopicDriftDetector.d.ts.map +1 -1
- package/dist/pipeline/TopicDriftDetector.js +143 -17
- package/dist/pipeline/TopicDriftDetector.js.map +1 -1
- package/dist/prompts/factRetrievalPrompt.d.ts +4 -0
- package/dist/prompts/factRetrievalPrompt.d.ts.map +1 -0
- package/dist/prompts/factRetrievalPrompt.js +25 -0
- package/dist/prompts/factRetrievalPrompt.js.map +1 -0
- package/dist/prompts/historyCompressionPrompt.d.ts +3 -0
- package/dist/prompts/historyCompressionPrompt.d.ts.map +1 -0
- package/dist/prompts/historyCompressionPrompt.js +4 -0
- package/dist/prompts/historyCompressionPrompt.js.map +1 -0
- package/dist/prompts/intentShiftPrompt.d.ts +3 -0
- package/dist/prompts/intentShiftPrompt.d.ts.map +1 -0
- package/dist/prompts/intentShiftPrompt.js +11 -0
- package/dist/prompts/intentShiftPrompt.js.map +1 -0
- package/dist/prompts/segmentExtractionPrompt.d.ts.map +1 -1
- package/dist/prompts/segmentExtractionPrompt.js +68 -12
- package/dist/prompts/segmentExtractionPrompt.js.map +1 -1
- package/dist/store/GraphStore.d.ts +16 -19
- package/dist/store/GraphStore.d.ts.map +1 -1
- package/dist/store/GraphStore.js +1 -576
- package/dist/store/GraphStore.js.map +1 -1
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +2 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/postgres-pgvector/GraphStore.d.ts +75 -0
- package/dist/store/postgres-pgvector/GraphStore.d.ts.map +1 -0
- package/dist/store/postgres-pgvector/GraphStore.js +782 -0
- package/dist/store/postgres-pgvector/GraphStore.js.map +1 -0
- package/dist/types.d.ts +69 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/drift/driftMarkers.d.ts +4 -0
- package/dist/utils/drift/driftMarkers.d.ts.map +1 -0
- package/dist/utils/drift/driftMarkers.js +31 -0
- package/dist/utils/drift/driftMarkers.js.map +1 -0
- package/dist/utils/drift/driftScore.d.ts +3 -0
- package/dist/utils/drift/driftScore.d.ts.map +1 -0
- package/dist/utils/drift/driftScore.js +11 -0
- package/dist/utils/drift/driftScore.js.map +1 -0
- package/dist/utils/drift/driftThreshold.d.ts +7 -0
- package/dist/utils/drift/driftThreshold.d.ts.map +1 -0
- package/dist/utils/drift/driftThreshold.js +21 -0
- package/dist/utils/drift/driftThreshold.js.map +1 -0
- package/dist/utils/drift/reentryMatch.d.ts +3 -0
- package/dist/utils/drift/reentryMatch.d.ts.map +1 -0
- package/dist/utils/drift/reentryMatch.js +10 -0
- package/dist/utils/drift/reentryMatch.js.map +1 -0
- package/dist/utils/extraction/segmentExtraction.d.ts +5 -0
- package/dist/utils/extraction/segmentExtraction.d.ts.map +1 -0
- package/dist/utils/extraction/segmentExtraction.js +82 -0
- package/dist/utils/extraction/segmentExtraction.js.map +1 -0
- package/dist/utils/reentry/reentryCues.d.ts +3 -0
- package/dist/utils/reentry/reentryCues.d.ts.map +1 -0
- package/dist/utils/reentry/reentryCues.js +14 -0
- package/dist/utils/reentry/reentryCues.js.map +1 -0
- package/dist/utils/reentry/reentryEdges.d.ts +16 -0
- package/dist/utils/reentry/reentryEdges.d.ts.map +1 -0
- package/dist/utils/reentry/reentryEdges.js +80 -0
- package/dist/utils/reentry/reentryEdges.js.map +1 -0
- package/dist/utils/reentry/reentrySimilarity.d.ts +5 -0
- package/dist/utils/reentry/reentrySimilarity.d.ts.map +1 -0
- package/dist/utils/reentry/reentrySimilarity.js +26 -0
- package/dist/utils/reentry/reentrySimilarity.js.map +1 -0
- package/dist/utils/reentry/reentryText.d.ts +6 -0
- package/dist/utils/reentry/reentryText.d.ts.map +1 -0
- package/dist/utils/reentry/reentryText.js +29 -0
- package/dist/utils/reentry/reentryText.js.map +1 -0
- package/dist/utils/reentry/types.d.ts +6 -0
- package/dist/utils/reentry/types.d.ts.map +1 -0
- package/dist/utils/reentry/types.js +2 -0
- package/dist/utils/reentry/types.js.map +1 -0
- package/dist/utils/text/normalizeText.d.ts +2 -0
- package/dist/utils/text/normalizeText.d.ts.map +1 -0
- package/dist/utils/text/normalizeText.js +5 -0
- package/dist/utils/text/normalizeText.js.map +1 -0
- package/dist/utils/text/terms.d.ts +3 -0
- package/dist/utils/text/terms.d.ts.map +1 -0
- package/dist/utils/text/terms.js +51 -0
- package/dist/utils/text/terms.js.map +1 -0
- package/dist/utils/text/tokenCount.d.ts +2 -0
- package/dist/utils/text/tokenCount.d.ts.map +1 -0
- package/dist/utils/text/tokenCount.js +4 -0
- package/dist/utils/text/tokenCount.js.map +1 -0
- package/dist/utils/vector/vectorLiteral.d.ts +3 -0
- package/dist/utils/vector/vectorLiteral.d.ts.map +1 -0
- package/dist/utils/vector/vectorLiteral.js +19 -0
- package/dist/utils/vector/vectorLiteral.js.map +1 -0
- package/package.json +11 -3
package/.env.example
CHANGED
package/README.md
CHANGED
|
@@ -1,95 +1,187 @@
|
|
|
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 conversations into topic-based memory, retrieves the relevant parts 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 tasks, or decide goals for an agent.
|
|
9
|
-
|
|
10
|
-
## What You Can Build
|
|
11
|
-
|
|
12
|
-
- Chatbots that remember user preferences across long conversations.
|
|
13
|
-
- Support or tutoring assistants that recall prior topics without replaying full history.
|
|
14
|
-
- Multi-chatbot demos where one bot can absorb selected memory from another.
|
|
15
|
-
- Prototypes for graph-based conversational memory, retrieval, and memory transfer.
|
|
16
|
-
- Server-side TypeScript apps that need reusable LLM memory primitives.
|
|
17
|
-
|
|
18
|
-
## How It Works
|
|
19
|
-
|
|
20
|
-
At a high level:
|
|
21
|
-
|
|
22
|
-
```text
|
|
23
|
-
chat messages
|
|
24
|
-
-> topic segments
|
|
25
|
-
-> memory nodes
|
|
26
|
-
-> graph links
|
|
27
|
-
-> relevant memory injection
|
|
28
|
-
-> optional memory grafting
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
MemoGrafter stores conversation turns, detects topic shifts, summarizes segments into memory nodes, links related nodes, and injects relevant memory into future LLM calls. Memory grafting lets one chatbot or session copy selected memory from another.
|
|
32
|
-
|
|
33
|
-
## Install
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npm install memo-grafter
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
MemoGrafter runs server-side on Node.js
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
DATABASE_URL=postgres://postgres:postgres@localhost:5432/memo_grafter
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
import
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
console.log(await agent.invoke("
|
|
68
|
-
|
|
69
|
-
await agent.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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 conversations into topic-based memory, retrieves the relevant parts 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 tasks, or decide goals for an agent.
|
|
9
|
+
|
|
10
|
+
## What You Can Build
|
|
11
|
+
|
|
12
|
+
- Chatbots that remember user preferences across long conversations.
|
|
13
|
+
- Support or tutoring assistants that recall prior topics without replaying full history.
|
|
14
|
+
- Multi-chatbot demos where one bot can absorb selected memory from another.
|
|
15
|
+
- Prototypes for graph-based conversational memory, retrieval, and memory transfer.
|
|
16
|
+
- Server-side TypeScript apps that need reusable LLM memory primitives.
|
|
17
|
+
|
|
18
|
+
## How It Works
|
|
19
|
+
|
|
20
|
+
At a high level:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
chat messages
|
|
24
|
+
-> topic segments
|
|
25
|
+
-> memory nodes
|
|
26
|
+
-> graph links
|
|
27
|
+
-> relevant memory injection
|
|
28
|
+
-> optional memory grafting
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
MemoGrafter stores conversation turns, detects topic shifts, summarizes segments into memory nodes, links related nodes, and injects relevant memory into future LLM calls. Drift detection uses embedding distance, sharp message pivots, short-message dampening, and structural phrases like "by the way" or "going back to" to split conversations into useful topic segments. Memory grafting lets one chatbot or session copy selected memory from another.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install memo-grafter
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
MemoGrafter runs server-side on Node.js. The built-in storage implementation is `PostgresGraphStore`, which requires PostgreSQL with `pgvector`. Included provider adapters require their matching API keys.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
DATABASE_URL=postgres://postgres:postgres@localhost:5432/memo_grafter
|
|
43
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
44
|
+
GEMINI_API_KEY=...
|
|
45
|
+
OPENAI_API_KEY=sk-...
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Minimal Example
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import "dotenv/config";
|
|
52
|
+
|
|
53
|
+
import {
|
|
54
|
+
MemoGrafterAgent,
|
|
55
|
+
OpenAIEmbedAdapter,
|
|
56
|
+
OpenAILLMAdapter,
|
|
57
|
+
} from "memo-grafter";
|
|
58
|
+
|
|
59
|
+
const agent = new MemoGrafterAgent({
|
|
60
|
+
db: { connectionString: process.env.DATABASE_URL! },
|
|
61
|
+
llm: new OpenAILLMAdapter("gpt-4o"),
|
|
62
|
+
embedder: new OpenAIEmbedAdapter("text-embedding-3-small"),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await agent.initialize();
|
|
66
|
+
|
|
67
|
+
console.log(await agent.invoke("I am planning a Japan trip."));
|
|
68
|
+
console.log(await agent.invoke("I like quiet towns, bookstores, and local cafes."));
|
|
69
|
+
console.log(await agent.invoke("What do you remember about my travel preferences?"));
|
|
70
|
+
|
|
71
|
+
const recall = await agent.recall("travel preferences", {
|
|
72
|
+
limit: 5,
|
|
73
|
+
minSimilarity: 0.6,
|
|
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
|
package/USER_GUIDE.md
CHANGED
|
@@ -12,11 +12,12 @@ The most important idea is memory grafting. A chatbot can build useful memory du
|
|
|
12
12
|
|
|
13
13
|
- Node.js 18 or newer.
|
|
14
14
|
- TypeScript or modern JavaScript using ES modules.
|
|
15
|
-
- PostgreSQL with the `pgvector` extension enabled
|
|
15
|
+
- PostgreSQL with the `pgvector` extension enabled for the built-in `PostgresGraphStore`.
|
|
16
16
|
- An LLM adapter.
|
|
17
17
|
- An embedding adapter.
|
|
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
|
+
- A Gemini API key only if using the included Gemini adapters.
|
|
20
21
|
- Redis only if enabling queue mode.
|
|
21
22
|
|
|
22
23
|
MemoGrafter is server-side only. Do not run it in browser code.
|
|
@@ -51,16 +52,19 @@ Create a `.env` file in your app:
|
|
|
51
52
|
```bash
|
|
52
53
|
DATABASE_URL=postgres://postgres:postgres@localhost:5432/memo_grafter
|
|
53
54
|
ANTHROPIC_API_KEY=sk-ant-...
|
|
55
|
+
GEMINI_API_KEY=...
|
|
54
56
|
OPENAI_API_KEY=sk-...
|
|
55
57
|
REDIS_URL=redis://localhost:6379
|
|
56
58
|
```
|
|
57
59
|
|
|
58
|
-
`DATABASE_URL` is required.
|
|
60
|
+
`DATABASE_URL` is required when using the built-in PostgreSQL storage.
|
|
59
61
|
|
|
60
62
|
`OPENAI_API_KEY` is required only when using `OpenAILLMAdapter` or `OpenAIEmbedAdapter`.
|
|
61
63
|
|
|
62
64
|
`ANTHROPIC_API_KEY` is required only when using `AnthropicLLMAdapter`.
|
|
63
65
|
|
|
66
|
+
`GEMINI_API_KEY` is required only when using `GeminiLLMAdapter` or `GeminiEmbedAdapter`.
|
|
67
|
+
|
|
64
68
|
`REDIS_URL` is optional and only needed when you pass `queue` config.
|
|
65
69
|
|
|
66
70
|
Enable `pgvector` in PostgreSQL:
|
|
@@ -69,7 +73,7 @@ Enable `pgvector` in PostgreSQL:
|
|
|
69
73
|
CREATE EXTENSION IF NOT EXISTS vector;
|
|
70
74
|
```
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
The built-in `PostgresGraphStore` creates its own tables during `initialize()`.
|
|
73
77
|
|
|
74
78
|
Current v1 tables:
|
|
75
79
|
|
|
@@ -77,6 +81,8 @@ Current v1 tables:
|
|
|
77
81
|
- `mg_segments`
|
|
78
82
|
- `mg_topic_nodes`
|
|
79
83
|
- `mg_topic_edges`
|
|
84
|
+
- `mg_memory_nodes`
|
|
85
|
+
- `mg_memory_edges`
|
|
80
86
|
- `mg_fleets`
|
|
81
87
|
- `mg_fleet_agents`
|
|
82
88
|
|
|
@@ -140,6 +146,14 @@ export interface Message {
|
|
|
140
146
|
|
|
141
147
|
A segment is a range of messages that belong to the same topic. MemoGrafter uses drift detection to decide where topic boundaries are.
|
|
142
148
|
|
|
149
|
+
Drift detection combines several signals:
|
|
150
|
+
|
|
151
|
+
- how far the current message is from the current topic embedding,
|
|
152
|
+
- whether the current message is a sharp pivot from the previous user message,
|
|
153
|
+
- whether the message contains structural phrases such as "by the way", "different topic", or "going back to",
|
|
154
|
+
- short-message dampening so filler like "okay" or "got it" is less likely to create false boundaries,
|
|
155
|
+
- optional LLM classification for ambiguous topic-shift scores.
|
|
156
|
+
|
|
143
157
|
Example:
|
|
144
158
|
|
|
145
159
|
```text
|
|
@@ -166,9 +180,29 @@ Important fields:
|
|
|
166
180
|
|
|
167
181
|
Topic nodes are stored in `mg_topic_nodes`.
|
|
168
182
|
|
|
183
|
+
### Memory Nodes
|
|
184
|
+
|
|
185
|
+
Memory nodes are typed atomic memories attached to topic nodes. They are the units used by targeted recall.
|
|
186
|
+
|
|
187
|
+
Important fields:
|
|
188
|
+
|
|
189
|
+
- `memoryType`: `"fact"`, `"insight"`, `"question"`, `"task"`, or `"reference"`.
|
|
190
|
+
- `subject`, `predicate`, `value`: the structured memory triple.
|
|
191
|
+
- `confidence`: confidence score from `0` to `1`.
|
|
192
|
+
- `topicNodeId`: parent topic node ID.
|
|
193
|
+
- `decayed`: whether the memory is stale.
|
|
194
|
+
- `supersededBy`: newer memory ID when this memory has been replaced.
|
|
195
|
+
|
|
196
|
+
Memory nodes are stored in `mg_memory_nodes`.
|
|
197
|
+
|
|
169
198
|
### Graph Edges
|
|
170
199
|
|
|
171
|
-
Edges connect related topic nodes. They can represent temporal, semantic, or
|
|
200
|
+
Edges connect related topic nodes. They can represent temporal, semantic, grafted, or reentry relationships.
|
|
201
|
+
|
|
202
|
+
- `temporal`: one topic followed another in the conversation.
|
|
203
|
+
- `semantic`: two topics are similar by embedding search.
|
|
204
|
+
- `grafted`: a topic was copied from another session or chatbot.
|
|
205
|
+
- `reentry`: the conversation returned to an earlier topic after discussing something else.
|
|
172
206
|
|
|
173
207
|
Edges are stored in `mg_topic_edges`.
|
|
174
208
|
|
|
@@ -226,6 +260,40 @@ On every call, `invoke()`:
|
|
|
226
260
|
|
|
227
261
|
On the first turn there may be no memory to inject. Later turns can use memory created from earlier turns.
|
|
228
262
|
|
|
263
|
+
### Targeted Recall
|
|
264
|
+
|
|
265
|
+
Use `recall()` when you want to retrieve structured memory by meaning without asking the LLM to produce an answer.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const result = await agent.recall("deployment config", {
|
|
269
|
+
limit: 8,
|
|
270
|
+
minSimilarity: 0.55,
|
|
271
|
+
tokenBudget: 1000,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
console.log(result.facts);
|
|
275
|
+
console.log(result.nodes);
|
|
276
|
+
console.log(result.systemPrompt);
|
|
277
|
+
console.log(result.tokenCount);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`recall()` returns a `RetrievalResult`:
|
|
281
|
+
|
|
282
|
+
- `facts`: matching memory nodes with a `similarity` score.
|
|
283
|
+
- `nodes`: parent topic nodes for the included facts.
|
|
284
|
+
- `systemPrompt`: a formatted memory block that can be passed to an LLM if you choose.
|
|
285
|
+
- `tokenCount`: approximate token count for the included fact blocks.
|
|
286
|
+
|
|
287
|
+
Options:
|
|
288
|
+
|
|
289
|
+
- `limit`: max memory nodes to fetch before filtering. Defaults to `10`.
|
|
290
|
+
- `minSimilarity`: cosine similarity floor. Defaults to `0.6`.
|
|
291
|
+
- `tokenBudget`: max approximate tokens for included fact blocks. Defaults to `1200`.
|
|
292
|
+
|
|
293
|
+
`recall()` is side-effect free. It does not call `invoke()`, does not trigger a new LLM completion, does not mutate local history, and does not inject the result automatically. Your application decides whether to display the memories, add `result.systemPrompt` to a model call, or ignore the result.
|
|
294
|
+
|
|
295
|
+
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.
|
|
296
|
+
|
|
229
297
|
## Inspecting Memory
|
|
230
298
|
|
|
231
299
|
Read active topic nodes:
|
|
@@ -368,8 +436,11 @@ const agent = new MemoGrafterAgent({
|
|
|
368
436
|
drift: {
|
|
369
437
|
mode: "intent",
|
|
370
438
|
windowSize: 5,
|
|
371
|
-
|
|
439
|
+
driftSensitivity: "medium",
|
|
372
440
|
minSegmentMessages: 3,
|
|
441
|
+
llmAmbiguityDetection: false,
|
|
442
|
+
reentryDetection: true,
|
|
443
|
+
reentryThreshold: 0.85,
|
|
373
444
|
},
|
|
374
445
|
graph: {
|
|
375
446
|
topK: 5,
|
|
@@ -390,7 +461,27 @@ db: {
|
|
|
390
461
|
}
|
|
391
462
|
```
|
|
392
463
|
|
|
393
|
-
PostgreSQL connection string
|
|
464
|
+
PostgreSQL connection string used by the built-in `PostgresGraphStore`.
|
|
465
|
+
|
|
466
|
+
MemoGrafter currently constructs `PostgresGraphStore` from this config internally. Advanced users can also import the storage contract directly:
|
|
467
|
+
|
|
468
|
+
```ts
|
|
469
|
+
import {
|
|
470
|
+
PostgresGraphStore,
|
|
471
|
+
type GraphStore,
|
|
472
|
+
} from "memo-grafter";
|
|
473
|
+
|
|
474
|
+
const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
`GraphStore` is the public storage interface. `PostgresGraphStore` is the default PostgreSQL and pgvector implementation.
|
|
478
|
+
|
|
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"`.
|
|
394
485
|
|
|
395
486
|
### `llm`
|
|
396
487
|
|
|
@@ -406,6 +497,12 @@ Anthropic models can be used with the included Anthropic adapter:
|
|
|
406
497
|
llm: new AnthropicLLMAdapter("claude-sonnet-4-5")
|
|
407
498
|
```
|
|
408
499
|
|
|
500
|
+
Gemini models can be used with the included Gemini adapter:
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
llm: new GeminiLLMAdapter("gemini-2.5-flash")
|
|
504
|
+
```
|
|
505
|
+
|
|
409
506
|
### `embedder`
|
|
410
507
|
|
|
411
508
|
```ts
|
|
@@ -416,14 +513,25 @@ Adapter used to create vectors for semantic search.
|
|
|
416
513
|
|
|
417
514
|
Anthropic does not provide a native embedding API. Pair `AnthropicLLMAdapter` with `OpenAIEmbedAdapter` or another custom `EmbedAdapter`.
|
|
418
515
|
|
|
516
|
+
Gemini embeddings can be used with the included Gemini embedder:
|
|
517
|
+
|
|
518
|
+
```ts
|
|
519
|
+
embedder: new GeminiEmbedAdapter("gemini-embedding-001")
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
`GeminiEmbedAdapter` requests 1536-dimensional embeddings by default to match MemoGrafter's current `vector(1536)` database schema. If you change the schema dimension, pass the matching value as the second constructor argument.
|
|
523
|
+
|
|
419
524
|
### `drift`
|
|
420
525
|
|
|
421
526
|
```ts
|
|
422
527
|
drift: {
|
|
423
528
|
mode: "intent",
|
|
424
529
|
windowSize: 5,
|
|
425
|
-
|
|
530
|
+
driftSensitivity: "medium",
|
|
426
531
|
minSegmentMessages: 3,
|
|
532
|
+
llmAmbiguityDetection: false,
|
|
533
|
+
reentryDetection: true,
|
|
534
|
+
reentryThreshold: 0.85,
|
|
427
535
|
}
|
|
428
536
|
```
|
|
429
537
|
|
|
@@ -431,11 +539,39 @@ Controls topic boundary detection.
|
|
|
431
539
|
|
|
432
540
|
- `mode`: `"intent"` or `"window"`.
|
|
433
541
|
- `windowSize`: message window size for window mode.
|
|
434
|
-
- `
|
|
542
|
+
- `driftSensitivity`: preferred sensitivity preset, one of `"low"`, `"medium"`, or `"high"`.
|
|
543
|
+
- `threshold`: deprecated numeric threshold. It still works when `driftSensitivity` is not set, but MemoGrafter logs a one-time warning.
|
|
435
544
|
- `minSegmentMessages`: minimum messages before a boundary.
|
|
545
|
+
- `llmAmbiguityDetection`: optional LLM check for borderline topic shifts. Defaults to `false`.
|
|
546
|
+
- `reentryDetection`: whether to link later topic returns back to earlier topic nodes. Defaults to `true`.
|
|
547
|
+
- `reentryThreshold`: embedding similarity threshold for reentry detection. Defaults to `0.85`.
|
|
436
548
|
|
|
437
549
|
Use `"intent"` for most chatbot memory demos. In intent mode, user messages drive topic shifts.
|
|
438
550
|
|
|
551
|
+
Sensitivity presets resolve internally to numeric thresholds:
|
|
552
|
+
|
|
553
|
+
- `"low"`: `0.25`
|
|
554
|
+
- `"medium"`: `0.35`
|
|
555
|
+
- `"high"`: `0.50`
|
|
556
|
+
|
|
557
|
+
Use `"medium"` first. Boundaries are cut when a drift score exceeds the resolved threshold, so lower numeric thresholds split more readily and higher numeric thresholds require stronger evidence.
|
|
558
|
+
|
|
559
|
+
If both `driftSensitivity` and `threshold` are provided, `driftSensitivity` wins.
|
|
560
|
+
|
|
561
|
+
#### Reentry Detection
|
|
562
|
+
|
|
563
|
+
Reentry detection handles conversations that leave a topic and later return to it:
|
|
564
|
+
|
|
565
|
+
```text
|
|
566
|
+
database choice -> authentication flow -> database connection pooling
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Without reentry detection, the later database discussion is just another topic node. With reentry detection, MemoGrafter creates a `reentry` edge from the later database node back to the earlier database node.
|
|
570
|
+
|
|
571
|
+
This helps graph traversal and memory injection recover earlier related context. A later question about connection pooling can still be connected to the original PostgreSQL/ACID discussion.
|
|
572
|
+
|
|
573
|
+
Reentry edges are written between the current rebuilt topic nodes. They do not point at deleted nodes from previous ingestion passes.
|
|
574
|
+
|
|
439
575
|
### `graph`
|
|
440
576
|
|
|
441
577
|
```ts
|
|
@@ -531,6 +667,34 @@ const agent = new MemoGrafterAgent({
|
|
|
531
667
|
|
|
532
668
|
Your embedding vector dimension must match the vector dimension expected by the database schema.
|
|
533
669
|
|
|
670
|
+
## Storage Backends
|
|
671
|
+
|
|
672
|
+
MemoGrafter exposes storage through the `GraphStore` interface. The built-in implementation is `PostgresGraphStore`, which stores relational data, graph edges, and vector search data in PostgreSQL with `pgvector`.
|
|
673
|
+
|
|
674
|
+
Most users do not need to instantiate a store directly. `MemoGrafter` and `MemoGrafterAgent` use `PostgresGraphStore` from the `db.connectionString` config:
|
|
675
|
+
|
|
676
|
+
```ts
|
|
677
|
+
const agent = new MemoGrafterAgent({
|
|
678
|
+
db: {
|
|
679
|
+
connectionString: process.env.DATABASE_URL!,
|
|
680
|
+
},
|
|
681
|
+
llm,
|
|
682
|
+
embedder,
|
|
683
|
+
});
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
If you are extending MemoGrafter, use `GraphStore` as the contract and keep concrete storage details behind an implementation:
|
|
687
|
+
|
|
688
|
+
```ts
|
|
689
|
+
import type { GraphStore } from "memo-grafter";
|
|
690
|
+
|
|
691
|
+
class MyGraphStore implements GraphStore {
|
|
692
|
+
// Implement the full GraphStore contract.
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
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
|
+
|
|
534
698
|
## Fleet API
|
|
535
699
|
|
|
536
700
|
Fleets let you group color-scoped worker chatbots and use a conductor to graft memory across workers.
|
|
@@ -641,11 +805,31 @@ For small demos, try:
|
|
|
641
805
|
```ts
|
|
642
806
|
drift: {
|
|
643
807
|
mode: "intent",
|
|
644
|
-
|
|
808
|
+
driftSensitivity: "medium",
|
|
645
809
|
minSegmentMessages: 3,
|
|
646
810
|
}
|
|
647
811
|
```
|
|
648
812
|
|
|
813
|
+
If segments are too coarse, try a lower resolved threshold:
|
|
814
|
+
|
|
815
|
+
```ts
|
|
816
|
+
drift: {
|
|
817
|
+
mode: "intent",
|
|
818
|
+
driftSensitivity: "low",
|
|
819
|
+
minSegmentMessages: 2,
|
|
820
|
+
}
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
If segments are too fragmented, try a higher resolved threshold:
|
|
824
|
+
|
|
825
|
+
```ts
|
|
826
|
+
drift: {
|
|
827
|
+
mode: "intent",
|
|
828
|
+
driftSensitivity: "high",
|
|
829
|
+
minSegmentMessages: 4,
|
|
830
|
+
}
|
|
831
|
+
```
|
|
832
|
+
|
|
649
833
|
### Absorb Copies Zero Nodes
|
|
650
834
|
|
|
651
835
|
Inspect the source memory:
|
|
@@ -664,6 +848,24 @@ await targetAgent.absorbFromAgent(sourceAgent, {
|
|
|
664
848
|
});
|
|
665
849
|
```
|
|
666
850
|
|
|
851
|
+
### Recall Returns Zero Facts
|
|
852
|
+
|
|
853
|
+
`recall()` searches atomic memory nodes, not raw chat messages. If it returns no facts:
|
|
854
|
+
|
|
855
|
+
- Make sure ingestion has completed.
|
|
856
|
+
- Confirm memory nodes exist for the session.
|
|
857
|
+
- Try a lower `minSimilarity`.
|
|
858
|
+
- Try a more specific query that uses the same vocabulary as the original conversation.
|
|
859
|
+
|
|
860
|
+
Example:
|
|
861
|
+
|
|
862
|
+
```ts
|
|
863
|
+
const result = await agent.recall("Japan travel preferences", {
|
|
864
|
+
minSimilarity: 0.3,
|
|
865
|
+
limit: 5,
|
|
866
|
+
});
|
|
867
|
+
```
|
|
868
|
+
|
|
667
869
|
### Redis Warnings
|
|
668
870
|
|
|
669
871
|
Redis is only required when you pass `queue` config. If you do not need background ingestion, remove the `queue` section.
|
|
@@ -697,10 +899,24 @@ Main exports:
|
|
|
697
899
|
- `WorkerAgent`
|
|
698
900
|
- `ConductorAgent`
|
|
699
901
|
- `AnthropicLLMAdapter`
|
|
902
|
+
- `GeminiLLMAdapter`
|
|
903
|
+
- `GeminiEmbedAdapter`
|
|
700
904
|
- `OpenAILLMAdapter`
|
|
701
905
|
- `OpenAIEmbedAdapter`
|
|
906
|
+
- `PostgresGraphStore`
|
|
907
|
+
- `GraphStore`
|
|
908
|
+
- `FleetAgentRecord`
|
|
909
|
+
- `RetrievalResult`
|
|
910
|
+
- `RetrieverConfig`
|
|
702
911
|
- public shared and fleet types
|
|
703
912
|
|
|
913
|
+
Useful `GraphStore` inspection methods:
|
|
914
|
+
|
|
915
|
+
- `getTopicNode(topicNodeId, sessionId?)`
|
|
916
|
+
- `getNodesBySession(sessionId)`
|
|
917
|
+
- `getSegmentsBySession(sessionId)`
|
|
918
|
+
- `getEdgesByType(sessionId, type)`
|
|
919
|
+
|
|
704
920
|
Common `MemoGrafterAgent` methods:
|
|
705
921
|
|
|
706
922
|
- `initialize()`: initialize storage.
|
|
@@ -709,6 +925,7 @@ Common `MemoGrafterAgent` methods:
|
|
|
709
925
|
- `getSessionId()`: read the current session ID.
|
|
710
926
|
- `getActiveNodes()`: inspect topic nodes.
|
|
711
927
|
- `getActiveSegments()`: inspect topic segments.
|
|
928
|
+
- `recall(query, options?)`: retrieve structured memory by semantic query.
|
|
712
929
|
- `graft(topicIds?)`: preview memory injection.
|
|
713
930
|
- `ingestGraftedNodes(nodes)`: copy provided nodes into this agent.
|
|
714
931
|
- `absorbFromAgent(sourceAgent, options)`: select and copy memory from another agent.
|
package/dist/MemoGrafter.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { GraphStore } from "./store/GraphStore.js";
|
|
2
1
|
import { MemoGrafterFleet } from "./fleet/MemoGrafterFleet.js";
|
|
3
2
|
import type { MemoGrafterFleetOptions } from "./fleet/types.js";
|
|
3
|
+
import type { GraphStore } from "./store/index.js";
|
|
4
4
|
import type { AbsorbFromAgentOptions, EmbedAdapter, InjectionResult, LLMAdapter, MemoGrafterConfig, Message, TopicNode, TopicSegment } from "./types.js";
|
|
5
5
|
export declare class MemoGrafter {
|
|
6
6
|
readonly llm: LLMAdapter;
|