memo-grafter 0.2.4 → 0.2.6
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 +5 -0
- package/USER_GUIDE.md +169 -10
- package/dist/MemoGrafter.d.ts +11 -5
- package/dist/MemoGrafter.d.ts.map +1 -1
- package/dist/MemoGrafter.js +36 -10
- package/dist/MemoGrafter.js.map +1 -1
- package/dist/MemoGrafterAgent.d.ts +7 -2
- package/dist/MemoGrafterAgent.d.ts.map +1 -1
- package/dist/MemoGrafterAgent.js +37 -4
- package/dist/MemoGrafterAgent.js.map +1 -1
- package/dist/crawler/VersioningPass.d.ts.map +1 -1
- package/dist/crawler/VersioningPass.js +7 -10
- package/dist/crawler/VersioningPass.js.map +1 -1
- package/dist/crawler/memoryMaintenance.d.ts +8 -0
- package/dist/crawler/memoryMaintenance.d.ts.map +1 -1
- package/dist/crawler/memoryMaintenance.js +111 -11
- package/dist/crawler/memoryMaintenance.js.map +1 -1
- package/dist/crawler/types.d.ts +1 -0
- package/dist/crawler/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/pipeline/IngestPipeline.d.ts +8 -3
- package/dist/pipeline/IngestPipeline.d.ts.map +1 -1
- package/dist/pipeline/IngestPipeline.js +49 -14
- package/dist/pipeline/IngestPipeline.js.map +1 -1
- package/dist/pipeline/RetrieverPipeline.d.ts +3 -0
- package/dist/pipeline/RetrieverPipeline.d.ts.map +1 -1
- package/dist/pipeline/RetrieverPipeline.js +56 -13
- package/dist/pipeline/RetrieverPipeline.js.map +1 -1
- package/dist/pipeline/SegmentProcessor.d.ts +2 -2
- package/dist/pipeline/SegmentProcessor.d.ts.map +1 -1
- package/dist/pipeline/SegmentProcessor.js +15 -9
- package/dist/pipeline/SegmentProcessor.js.map +1 -1
- package/dist/prompts/segmentExtractionPrompt.d.ts +1 -1
- package/dist/prompts/segmentExtractionPrompt.d.ts.map +1 -1
- package/dist/prompts/segmentExtractionPrompt.js +7 -1
- package/dist/prompts/segmentExtractionPrompt.js.map +1 -1
- package/dist/queue/IngestQueue.d.ts +3 -2
- package/dist/queue/IngestQueue.d.ts.map +1 -1
- package/dist/queue/IngestQueue.js +22 -2
- package/dist/queue/IngestQueue.js.map +1 -1
- package/dist/store/GraphStore.d.ts +4 -3
- package/dist/store/GraphStore.d.ts.map +1 -1
- package/dist/store/postgres-pgvector/GraphStore.d.ts +6 -3
- package/dist/store/postgres-pgvector/GraphStore.d.ts.map +1 -1
- package/dist/store/postgres-pgvector/GraphStore.js +84 -6
- package/dist/store/postgres-pgvector/GraphStore.js.map +1 -1
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/drift/adaptiveDriftSensitivity.d.ts +20 -0
- package/dist/utils/drift/adaptiveDriftSensitivity.d.ts.map +1 -0
- package/dist/utils/drift/adaptiveDriftSensitivity.js +67 -0
- package/dist/utils/drift/adaptiveDriftSensitivity.js.map +1 -0
- package/dist/utils/tags.d.ts +2 -0
- package/dist/utils/tags.d.ts.map +1 -0
- package/dist/utils/tags.js +13 -0
- package/dist/utils/tags.js.map +1 -0
- package/dist/utils/text/splitTextForIngestion.d.ts +2 -0
- package/dist/utils/text/splitTextForIngestion.d.ts.map +1 -0
- package/dist/utils/text/splitTextForIngestion.js +37 -0
- package/dist/utils/text/splitTextForIngestion.js.map +1 -0
- package/dist/utils/text/splitTextForIngestion.test.d.ts +2 -0
- package/dist/utils/text/splitTextForIngestion.test.d.ts.map +1 -0
- package/dist/utils/text/splitTextForIngestion.test.js +19 -0
- package/dist/utils/text/splitTextForIngestion.test.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ MemoGrafter builds the memory graph incrementally. New chatbot turns append topi
|
|
|
17
17
|
## What It Is For
|
|
18
18
|
|
|
19
19
|
- Chatbots that need long-running memory.
|
|
20
|
+
- Editors, document imports, and transcripts that need memory without assistant responses.
|
|
20
21
|
- Assistants that should recall user preferences, prior context, and open questions.
|
|
21
22
|
- Multi-chatbot or multi-session flows where selected memory can be grafted into another conversation.
|
|
22
23
|
- TypeScript apps that need reusable memory, retrieval, and graph-backed conversation primitives.
|
|
@@ -63,6 +64,10 @@ await agent.initialize();
|
|
|
63
64
|
await agent.invoke("I am planning a Japan trip.");
|
|
64
65
|
await agent.invoke("I like quiet towns, bookstores, and local cafes.");
|
|
65
66
|
|
|
67
|
+
await agent.ingestText("The product roadmap now prioritizes document imports.", {
|
|
68
|
+
source: "import",
|
|
69
|
+
});
|
|
70
|
+
|
|
66
71
|
const recall = await agent.recall("travel preferences");
|
|
67
72
|
console.log(recall.facts);
|
|
68
73
|
|
package/USER_GUIDE.md
CHANGED
|
@@ -90,6 +90,8 @@ Current v1 tables:
|
|
|
90
90
|
- `mg_session_ingest_state`
|
|
91
91
|
- `mg_graft_registry`
|
|
92
92
|
|
|
93
|
+
`mg_topic_nodes` and `mg_memory_nodes` include optional `tags TEXT[]` columns. Tags default to an empty array, so existing untagged sessions continue to work normally.
|
|
94
|
+
|
|
93
95
|
## Quick Start
|
|
94
96
|
|
|
95
97
|
Create `src/index.ts`:
|
|
@@ -179,6 +181,7 @@ Important fields:
|
|
|
179
181
|
- `label`: short label.
|
|
180
182
|
- `summary`: structured summary of the segment.
|
|
181
183
|
- `embedding`: vector used for semantic search.
|
|
184
|
+
- `tags`: optional normalized tags such as `"project:memo-grafter"` or `"planning"`.
|
|
182
185
|
- `messageRange`: source message range.
|
|
183
186
|
- `topicOrder`: chronological order.
|
|
184
187
|
- `driftScore`: topic-change score.
|
|
@@ -196,6 +199,7 @@ Important fields:
|
|
|
196
199
|
- `subject`, `predicate`, `value`: the structured memory triple.
|
|
197
200
|
- `confidence`: confidence score from `0` to `1`.
|
|
198
201
|
- `topicNodeId`: parent topic node ID.
|
|
202
|
+
- `tags`: optional normalized tags copied from the session or ingest call.
|
|
199
203
|
- `decayed`: whether the memory is stale.
|
|
200
204
|
- `hasConflict`: whether crawler maintenance found a conflicting active fact.
|
|
201
205
|
- `supersededBy`: newer memory ID when this memory has been replaced.
|
|
@@ -292,6 +296,34 @@ On the first turn there may be no memory to recall. Later turns can use memory c
|
|
|
292
296
|
|
|
293
297
|
Normal ingestion does not call `clearSession()`. Existing topic nodes, grafted nodes, memory rows, and graph edges are preserved unless you explicitly clear the session.
|
|
294
298
|
|
|
299
|
+
### Ingesting Text Without A Conversation
|
|
300
|
+
|
|
301
|
+
Use `ingestText()` to build topic nodes, memory nodes, and graph edges from raw text without generating an assistant response:
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
await agent.ingestText(editorContent, {
|
|
305
|
+
replace: true,
|
|
306
|
+
label: "Morning entry",
|
|
307
|
+
source: "classic-editor",
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
The extraction LLM is still used internally to summarize the text and extract structured memories. The raw text is stored for graph ingestion but is not added to `getHistory()` and is not sent through the normal assistant response-generation path.
|
|
312
|
+
|
|
313
|
+
Before ingestion, MemoGrafter splits the text into internal chunks using line and sentence boundaries, with a size limit for unusually long sentences. The existing drift detector runs across those chunks, so one `ingestText()` call can create multiple topic segments and topic nodes when the text changes subject. These chunks remain internal and do not appear in `getHistory()`.
|
|
314
|
+
|
|
315
|
+
Options:
|
|
316
|
+
|
|
317
|
+
- `replace`: clear the current session's stored graph before ingesting the text. Defaults to `false`.
|
|
318
|
+
- `label`: optional hint for the first topic label created by this ingestion.
|
|
319
|
+
- `source`: optional arbitrary metadata stored on created topic and memory nodes, such as `"import"` or `"classic-editor"`.
|
|
320
|
+
|
|
321
|
+
Use `replace: true` for autosave workflows where each call represents the complete current document. Replacement removes stored messages, topic nodes, memory nodes, graph edges, segments, grafted nodes, and the ingest cursor for the current session. It does not clear the agent's public conversational history or change the session ID.
|
|
322
|
+
|
|
323
|
+
Without `replace`, each text call appends to the current session memory. Later `invoke()` calls continue to work normally and can recall facts extracted from the ingested text.
|
|
324
|
+
|
|
325
|
+
In queue mode, `await agent.ingestText()` confirms that the ingestion job was queued. Reads may need to wait for the worker to finish before the new graph content is visible.
|
|
326
|
+
|
|
295
327
|
### Clearing A Session
|
|
296
328
|
|
|
297
329
|
Use `clearSession()` when you intentionally want to reset an agent:
|
|
@@ -302,6 +334,32 @@ await agent.clearSession();
|
|
|
302
334
|
|
|
303
335
|
This waits for pending ingestion, clears the agent's local in-memory history, removes stored messages, topic nodes, memory nodes, graph edges, segments, and resets the session ingest cursor. It is a destructive operation and is not part of normal `invoke()` processing.
|
|
304
336
|
|
|
337
|
+
### Session Tags
|
|
338
|
+
|
|
339
|
+
Use session tags when you want to organize memory by project, planning area, week, domain, or worker route.
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
await agent.setSessionTags([
|
|
343
|
+
"project:memo-grafter",
|
|
344
|
+
"planning",
|
|
345
|
+
"week:2026-05-25",
|
|
346
|
+
]);
|
|
347
|
+
|
|
348
|
+
console.log(agent.getSessionTags());
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Tags are optional. They are normalized by trimming whitespace, lowercasing, deduplicating, and sorting. Calling `setSessionTags()` waits for pending ingestion, updates existing topic and memory rows for the current session, and applies the same tags to future memories created by `invoke()`.
|
|
352
|
+
|
|
353
|
+
You can also tag direct ingestion:
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
await memo.ingest(messages, sessionId, {
|
|
357
|
+
tags: ["project:memo-grafter", "planning"],
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Tags do not replace `sessionId`. By default, MemoGrafter still reads from the current session. Tag filters are opt-in.
|
|
362
|
+
|
|
305
363
|
### Targeted Recall
|
|
306
364
|
|
|
307
365
|
Use `recall()` when you want to retrieve structured memory by meaning without asking the LLM to produce an answer.
|
|
@@ -311,6 +369,13 @@ const result = await agent.recall("deployment config", {
|
|
|
311
369
|
limit: 8,
|
|
312
370
|
minSimilarity: 0.55,
|
|
313
371
|
tokenBudget: 1000,
|
|
372
|
+
tags: ["project:memo-grafter"],
|
|
373
|
+
tagMode: "all",
|
|
374
|
+
scope: "session-and-tags",
|
|
375
|
+
scoring: {
|
|
376
|
+
similarityWeight: 0.7,
|
|
377
|
+
confidenceWeight: 0.3,
|
|
378
|
+
},
|
|
314
379
|
cache: {
|
|
315
380
|
ttlSeconds: 90,
|
|
316
381
|
},
|
|
@@ -334,9 +399,26 @@ Options:
|
|
|
334
399
|
- `limit`: max memory nodes to fetch before filtering. Defaults to `10`.
|
|
335
400
|
- `minSimilarity`: cosine similarity floor. Defaults to `0.6`.
|
|
336
401
|
- `tokenBudget`: max approximate tokens for included fact blocks. Defaults to `1200`.
|
|
402
|
+
- `tags`: optional normalized tag filter.
|
|
403
|
+
- `tagMode`: `"all"` requires every requested tag, `"any"` accepts at least one requested tag. Defaults to `"all"`.
|
|
404
|
+
- `scope`: `"session"` keeps normal current-session recall, `"session-and-tags"` filters current-session recall by tags, and `"tagged"` searches across sessions matching the tags.
|
|
405
|
+
- `scoring.similarityWeight`: weight applied to semantic similarity when ranking retrieved facts. Defaults to `0.7`.
|
|
406
|
+
- `scoring.confidenceWeight`: weight applied to memory confidence when ranking retrieved facts. Defaults to `0.3`.
|
|
337
407
|
- `cache.ttlSeconds`: per-call recall cache TTL override when `MemoGrafterConfig.cache` is enabled. Values are clamped to 60-120 seconds.
|
|
338
408
|
|
|
339
|
-
`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.
|
|
409
|
+
`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. Retrieval still uses `minSimilarity` for the vector search floor, then ranks returned active facts with `similarity * similarityWeight + confidence * confidenceWeight`.
|
|
410
|
+
|
|
411
|
+
Cross-session tagged recall is explicit:
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
const projectMemory = await agent.recall("deployment decisions", {
|
|
415
|
+
tags: ["project:memo-grafter"],
|
|
416
|
+
scope: "tagged",
|
|
417
|
+
minSimilarity: 0.3,
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
This can return matching active memories from older or different sessions with the same tag. If your development database contains repeated smoke-test runs, you may see more than one matching fact because the older tagged rows are still present.
|
|
340
422
|
|
|
341
423
|
`MemoGrafterAgent.invoke()` also calls `recall()` internally before answering when the session has topic nodes. In that automatic path, the returned `systemPrompt` is pinned as a single system message before the recent raw chat window. Automatic recall uses `inject.recallLimit` and `inject.recallMinSimilarity`, defaulting to `6` and `0.55`.
|
|
342
424
|
|
|
@@ -391,6 +473,15 @@ for (const node of nodes) {
|
|
|
391
473
|
}
|
|
392
474
|
```
|
|
393
475
|
|
|
476
|
+
Filter active topic nodes by tag:
|
|
477
|
+
|
|
478
|
+
```ts
|
|
479
|
+
const planningNodes = await agent.getActiveNodes({
|
|
480
|
+
tags: ["planning"],
|
|
481
|
+
tagMode: "all",
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
394
485
|
Read active segments:
|
|
395
486
|
|
|
396
487
|
```ts
|
|
@@ -498,14 +589,20 @@ await memo.close();
|
|
|
498
589
|
|
|
499
590
|
`start()` is safe to call more than once; it does not create duplicate intervals. If a scheduled tick fires while the previous run is still executing, that tick is skipped.
|
|
500
591
|
|
|
501
|
-
The built-in conflict
|
|
592
|
+
The built-in conflict and versioning passes use separate deterministic classifiers:
|
|
502
593
|
|
|
503
|
-
-
|
|
504
|
-
- a group conflicts when it has different normalized `value` strings;
|
|
594
|
+
- both classifiers begin by grouping active memories by session, normalized `subject`, and normalized `predicate`;
|
|
595
|
+
- a group conflicts when it has different normalized `value` strings and the newest value does not contain an explicit update cue;
|
|
596
|
+
- a group versions only when its newest value contains an explicit replacement or update cue such as `actually`, `now`, `changed to`, or `instead`;
|
|
505
597
|
- decayed memories are skipped;
|
|
506
598
|
- already superseded memories are skipped;
|
|
507
|
-
-
|
|
508
|
-
-
|
|
599
|
+
- broad topic memories with generic subject/predicate pairs are skipped unless they match a recognized competing trip-plan pattern;
|
|
600
|
+
- version replacement candidates are selected by `createdAt`;
|
|
601
|
+
- if version candidate timestamps tie, deterministic ID ordering is used.
|
|
602
|
+
|
|
603
|
+
Conflict detection is meant for mutually exclusive fact slots such as `user location Delhi` versus `user location Bangalore`. For generic "things discussed" memories, MemoGrafter only recognizes a narrow travel destination plan pattern by default. That means `Goa trip plan` and `Vietnam trip plan` can conflict, while `how to cook rajma chawal`, `food in Vietnam`, and `places to visit Vietnam` do not all conflict just because extraction used a generic subject and predicate.
|
|
604
|
+
|
|
605
|
+
Versioning is meant for explicit replacements such as `user location Delhi` followed by `user location Actually Bangalore now`. The extraction prompt asks adapters to preserve update cues in memory values because the built-in crawler operates on stored memory rows and does not inspect the original conversation text.
|
|
509
606
|
|
|
510
607
|
`DecayScoringPass` uses confidence-weighted exponential recency decay:
|
|
511
608
|
|
|
@@ -529,12 +626,18 @@ When conflicts are found:
|
|
|
529
626
|
|
|
530
627
|
- both active facts get `hasConflict: true`;
|
|
531
628
|
- a `conflicts` memory edge is created;
|
|
629
|
+
- neither fact is superseded.
|
|
630
|
+
|
|
631
|
+
When explicit replacements are found:
|
|
632
|
+
|
|
532
633
|
- the older fact gets `supersededBy` pointing to the newer fact;
|
|
533
634
|
- an `updates` edge is created as `newer_memory --updates--> older_memory`.
|
|
534
635
|
- stale active memories can be marked `decayed: true` by the decay pass.
|
|
535
636
|
|
|
536
637
|
Crawler maintenance is non-destructive. It annotates existing memory rows and creates memory edges. It does not delete nodes, does not rebuild topics, and does not rewrite topic summaries.
|
|
537
638
|
|
|
639
|
+
Existing incorrect conflict edges are not automatically deleted. If an older crawler run created a false-positive edge, handle cleanup with a future explicit pruning pass or filter displayed memory edges to active memories in your app.
|
|
640
|
+
|
|
538
641
|
Do not put crawler behavior inside `clearSession()`. `clearSession()` is a destructive reset. The crawler is a non-destructive maintenance worker. If you intentionally rebuild a session graph, use this order:
|
|
539
642
|
|
|
540
643
|
```ts
|
|
@@ -653,6 +756,9 @@ const agent = new MemoGrafterAgent({
|
|
|
653
756
|
mode: "intent",
|
|
654
757
|
windowSize: 5,
|
|
655
758
|
driftSensitivity: "medium",
|
|
759
|
+
adaptiveSensitivity: {
|
|
760
|
+
enabled: false,
|
|
761
|
+
},
|
|
656
762
|
minSegmentMessages: 3,
|
|
657
763
|
llmAmbiguityDetection: false,
|
|
658
764
|
reentryDetection: true,
|
|
@@ -701,7 +807,7 @@ const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
|
|
|
701
807
|
|
|
702
808
|
Useful store inspection methods include:
|
|
703
809
|
|
|
704
|
-
- `getNodesBySession(sessionId)`: read topic nodes for a session.
|
|
810
|
+
- `getNodesBySession(sessionId, options?)`: read topic nodes for a session, optionally filtered by tags.
|
|
705
811
|
- `getTopicNode(topicNodeId, sessionId?)`: read one topic node by ID.
|
|
706
812
|
- `getSegmentsBySession(sessionId)`: read topic segments for a session.
|
|
707
813
|
- `getEdgesByType(sessionId, type)`: inspect graph edges such as `"reentry"`, `"semantic"`, `"temporal"`, or `"grafted"`.
|
|
@@ -709,6 +815,7 @@ Useful store inspection methods include:
|
|
|
709
815
|
- `getMemoriesBySession(sessionId)`: read all memory nodes for a session, including decayed and superseded rows.
|
|
710
816
|
- `getMemoryEdgesBySession(sessionId)`: read memory-level edges such as `conflicts` and `updates`.
|
|
711
817
|
- `getGraftRegistry(sessionId)`: read graft provenance entries for a session.
|
|
818
|
+
- `setSessionTags(sessionId, tags)`: replace the normalized tag set on existing topic and memory rows for a session.
|
|
712
819
|
|
|
713
820
|
### `llm`
|
|
714
821
|
|
|
@@ -755,6 +862,9 @@ drift: {
|
|
|
755
862
|
mode: "intent",
|
|
756
863
|
windowSize: 5,
|
|
757
864
|
driftSensitivity: "medium",
|
|
865
|
+
adaptiveSensitivity: {
|
|
866
|
+
enabled: false,
|
|
867
|
+
},
|
|
758
868
|
minSegmentMessages: 3,
|
|
759
869
|
llmAmbiguityDetection: false,
|
|
760
870
|
reentryDetection: true,
|
|
@@ -767,6 +877,7 @@ Controls topic boundary detection.
|
|
|
767
877
|
- `mode`: `"intent"` or `"window"`.
|
|
768
878
|
- `windowSize`: message window size for window mode.
|
|
769
879
|
- `driftSensitivity`: preferred sensitivity preset, one of `"low"`, `"medium"`, or `"high"`.
|
|
880
|
+
- `adaptiveSensitivity`: optional session-history based threshold tuning. Disabled by default.
|
|
770
881
|
- `threshold`: deprecated numeric threshold. It still works when `driftSensitivity` is not set, but MemoGrafter logs a one-time warning.
|
|
771
882
|
- `minSegmentMessages`: minimum messages before a boundary.
|
|
772
883
|
- `llmAmbiguityDetection`: optional LLM check for borderline topic shifts. Defaults to `false`.
|
|
@@ -785,6 +896,30 @@ Use `"medium"` first. Boundaries are cut when a drift score exceeds the resolved
|
|
|
785
896
|
|
|
786
897
|
If both `driftSensitivity` and `threshold` are provided, `driftSensitivity` wins.
|
|
787
898
|
|
|
899
|
+
#### Adaptive Sensitivity
|
|
900
|
+
|
|
901
|
+
Adaptive sensitivity is opt-in and keeps the configured `driftSensitivity` as its baseline. When enabled, MemoGrafter looks at recent saved segments for the session and nudges the resolved threshold by a small bounded step:
|
|
902
|
+
|
|
903
|
+
```ts
|
|
904
|
+
drift: {
|
|
905
|
+
mode: "intent",
|
|
906
|
+
driftSensitivity: "medium",
|
|
907
|
+
adaptiveSensitivity: {
|
|
908
|
+
enabled: true,
|
|
909
|
+
minSegments: 4,
|
|
910
|
+
lookbackSegments: 8,
|
|
911
|
+
targetSegmentMessages: {
|
|
912
|
+
min: 3,
|
|
913
|
+
max: 8,
|
|
914
|
+
},
|
|
915
|
+
adjustmentStep: 0.05,
|
|
916
|
+
maxAdjustment: 0.1,
|
|
917
|
+
},
|
|
918
|
+
}
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
If recent segments are consistently short, MemoGrafter raises the threshold slightly to reduce fragmentation. If recent segments are consistently long, it lowers the threshold slightly to split more readily. It does not adapt until enough segment history exists, and it skips adjustment when recent segment lengths are too erratic.
|
|
922
|
+
|
|
788
923
|
#### Reentry Detection
|
|
789
924
|
|
|
790
925
|
Reentry detection handles conversations that leave a topic and later return to it:
|
|
@@ -851,6 +986,20 @@ Enables an opt-in Redis cache for targeted recall. MemoGrafter creates one share
|
|
|
851
986
|
|
|
852
987
|
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.
|
|
853
988
|
|
|
989
|
+
When tag-aware recall is used, cache keys also include recall `scope`, `tagMode`, and the normalized tag list. This prevents untagged, session-filtered, and cross-session tagged recall from sharing cached search results.
|
|
990
|
+
|
|
991
|
+
## Manual Smoke Tests
|
|
992
|
+
|
|
993
|
+
From this repository, run the session-tagging smoke with a real PostgreSQL database:
|
|
994
|
+
|
|
995
|
+
```powershell
|
|
996
|
+
npx tsx --env-file=.env ./tests/manual/graft/session-tags-smoke.ts
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
Use the forward-slash path in PowerShell. An unquoted backslash path can be collapsed before `tsx` receives it.
|
|
1000
|
+
|
|
1001
|
+
The smoke creates two tagged sessions, writes one memory into each, verifies current-session tag filtering with `getActiveNodes()`, and verifies cross-session project recall with `recall(..., { scope: "tagged" })`. If you run it repeatedly against the same database, tagged recall can return rows from previous smoke runs because those historical sessions are still present.
|
|
1002
|
+
|
|
854
1003
|
## Queue Mode
|
|
855
1004
|
|
|
856
1005
|
Without queue config, ingestion runs synchronously after `invoke()`.
|
|
@@ -983,6 +1132,10 @@ const retriever = new RetrieverPipeline(store, embedder, {
|
|
|
983
1132
|
limit: 8,
|
|
984
1133
|
minSimilarity: 0.55,
|
|
985
1134
|
tokenBudget: 1000,
|
|
1135
|
+
scoring: {
|
|
1136
|
+
similarityWeight: 0.7,
|
|
1137
|
+
confidenceWeight: 0.3,
|
|
1138
|
+
},
|
|
986
1139
|
});
|
|
987
1140
|
|
|
988
1141
|
const result = await retriever.run(
|
|
@@ -1250,12 +1403,15 @@ Main exports:
|
|
|
1250
1403
|
- `FleetAgentRecord`
|
|
1251
1404
|
- `RetrievalResult`
|
|
1252
1405
|
- `RetrieverConfig`
|
|
1406
|
+
- `TagFilterOptions`
|
|
1407
|
+
- `IngestOptions`
|
|
1408
|
+
- `IngestTextOptions`
|
|
1253
1409
|
- public shared and fleet types
|
|
1254
1410
|
|
|
1255
1411
|
Useful `GraphStore` inspection methods:
|
|
1256
1412
|
|
|
1257
1413
|
- `getTopicNode(topicNodeId, sessionId?)`
|
|
1258
|
-
- `getNodesBySession(sessionId)`
|
|
1414
|
+
- `getNodesBySession(sessionId, options?)`
|
|
1259
1415
|
- `getSegmentsBySession(sessionId)`
|
|
1260
1416
|
- `getEdgesByType(sessionId, type)`
|
|
1261
1417
|
- `getEdgesBySession(sessionId)`
|
|
@@ -1268,14 +1424,17 @@ Common `MemoGrafterAgent` methods:
|
|
|
1268
1424
|
|
|
1269
1425
|
- `initialize()`: initialize storage.
|
|
1270
1426
|
- `invoke(message)`: send a user message and receive an assistant response.
|
|
1427
|
+
- `ingestText(text, options?)`: ingest raw text without generating an assistant response.
|
|
1271
1428
|
- `getHistory()`: read local chat history.
|
|
1272
1429
|
- `getSessionId()`: read the current session ID.
|
|
1273
1430
|
- `getGraphSnapshot()`: read nodes, edges, memories, session ID, and capture timestamp for visualization or inspection.
|
|
1274
1431
|
- `getGraftRegistry()`: inspect provenance for grafted nodes in the current session.
|
|
1275
|
-
- `getActiveNodes()`: inspect topic nodes.
|
|
1432
|
+
- `getActiveNodes(options?)`: inspect topic nodes, optionally filtered by tags.
|
|
1276
1433
|
- `getActiveSegments()`: inspect topic segments.
|
|
1434
|
+
- `setSessionTags(tags)`: replace tags on the current session and apply them to future ingested memories.
|
|
1435
|
+
- `getSessionTags()`: read the current agent's normalized session tags.
|
|
1277
1436
|
- `clearSession()`: explicitly clear local history and stored session memory.
|
|
1278
|
-
- `recall(query, options?)`: retrieve structured memory by semantic query.
|
|
1437
|
+
- `recall(query, options?)`: retrieve structured memory by semantic query, optionally filtered by tags.
|
|
1279
1438
|
- `graft(topicIds?)`: preview memory injection.
|
|
1280
1439
|
- `ingestGraftedNodes(nodes)`: copy provided nodes into this agent.
|
|
1281
1440
|
- `absorbFromAgent(sourceAgent, options)`: select and copy memory from another agent.
|
package/dist/MemoGrafter.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Redis } from "ioredis";
|
|
|
2
2
|
import { MemoGrafterFleet } from "./fleet/MemoGrafterFleet.js";
|
|
3
3
|
import type { MemoGrafterFleetOptions } from "./fleet/types.js";
|
|
4
4
|
import type { GraphStore } from "./store/index.js";
|
|
5
|
-
import type { AbsorbFromAgentOptions, EmbedAdapter, InjectionResult, LLMAdapter, MemoGrafterConfig, Message, TopicNode, TopicSegment } from "./types.js";
|
|
5
|
+
import type { AbsorbFromAgentOptions, EmbedAdapter, IngestOptions, IngestTextOptions, InjectionResult, LLMAdapter, MemoGrafterConfig, Message, TopicNode, TopicSegment } from "./types.js";
|
|
6
6
|
export declare class MemoGrafter {
|
|
7
7
|
readonly llm: LLMAdapter;
|
|
8
8
|
readonly embedder: EmbedAdapter;
|
|
@@ -13,10 +13,15 @@ export declare class MemoGrafter {
|
|
|
13
13
|
private readonly ingestQueue;
|
|
14
14
|
constructor(config: MemoGrafterConfig);
|
|
15
15
|
initialize(): Promise<void>;
|
|
16
|
-
ingest(messages: Message[], sessionId: string): Promise<TopicNode[]>;
|
|
17
|
-
ingestNow(messages: Message[], sessionId: string): Promise<TopicNode[]>;
|
|
18
|
-
enqueueIngest(messages: Message[], sessionId: string): Promise<void>;
|
|
19
|
-
|
|
16
|
+
ingest(messages: Message[], sessionId: string, options?: IngestOptions): Promise<TopicNode[]>;
|
|
17
|
+
ingestNow(messages: Message[], sessionId: string, options?: IngestOptions): Promise<TopicNode[]>;
|
|
18
|
+
enqueueIngest(messages: Message[], sessionId: string, options?: IngestOptions): Promise<void>;
|
|
19
|
+
ingestText(text: string, sessionId: string, options?: IngestTextOptions & IngestOptions): Promise<TopicNode[]>;
|
|
20
|
+
enqueueTextIngest(text: string, sessionId: string, options?: IngestTextOptions & IngestOptions): Promise<void>;
|
|
21
|
+
getTopics(sessionId: string, options?: {
|
|
22
|
+
tags?: string[];
|
|
23
|
+
tagMode?: "all" | "any";
|
|
24
|
+
}): Promise<{
|
|
20
25
|
nodes: TopicNode[];
|
|
21
26
|
segments: TopicSegment[];
|
|
22
27
|
}>;
|
|
@@ -27,5 +32,6 @@ export declare class MemoGrafter {
|
|
|
27
32
|
createFleet(options?: MemoGrafterFleetOptions): MemoGrafterFleet;
|
|
28
33
|
close(): Promise<void>;
|
|
29
34
|
private assertServerEnvironment;
|
|
35
|
+
private toTextPipelineOptions;
|
|
30
36
|
}
|
|
31
37
|
//# sourceMappingURL=MemoGrafter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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,aAAa,EAEb,iBAAiB,EACjB,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;IAqDrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQjG,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAI9F,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASvG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAiB,GAAG,aAAkB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAS5G,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAiB,GAAG,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,GAAG,KAAK,CAAA;KAAO,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IAMzJ,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;IAW/B,OAAO,CAAC,qBAAqB;CAS9B"}
|
package/dist/MemoGrafter.js
CHANGED
|
@@ -22,6 +22,7 @@ export class MemoGrafter {
|
|
|
22
22
|
const llmAmbiguityDetection = config.drift?.llmAmbiguityDetection;
|
|
23
23
|
const reentryDetection = config.drift?.reentryDetection;
|
|
24
24
|
const reentryThreshold = config.drift?.reentryThreshold;
|
|
25
|
+
const adaptiveSensitivity = config.drift?.adaptiveSensitivity;
|
|
25
26
|
const topK = config.graph?.topK ?? 5;
|
|
26
27
|
const hopDepth = config.graph?.hopDepth ?? 1;
|
|
27
28
|
const bufferSize = config.inject?.bufferSize ?? 1;
|
|
@@ -51,6 +52,7 @@ export class MemoGrafter {
|
|
|
51
52
|
...(llmAmbiguityDetection !== undefined ? { llmAmbiguityDetection } : {}),
|
|
52
53
|
...(reentryDetection !== undefined ? { reentryDetection } : {}),
|
|
53
54
|
...(reentryThreshold !== undefined ? { reentryThreshold } : {}),
|
|
55
|
+
...(adaptiveSensitivity !== undefined ? { adaptiveSensitivity } : {}),
|
|
54
56
|
});
|
|
55
57
|
this.grafterPipeline = new GrafterPipeline(this.store, {
|
|
56
58
|
hopDepth,
|
|
@@ -62,24 +64,39 @@ export class MemoGrafter {
|
|
|
62
64
|
initialize() {
|
|
63
65
|
return this.store.initialize();
|
|
64
66
|
}
|
|
65
|
-
ingest(messages, sessionId) {
|
|
67
|
+
ingest(messages, sessionId, options = {}) {
|
|
66
68
|
if (this.ingestQueue) {
|
|
67
|
-
return this.enqueueIngest(messages, sessionId).then(() => []);
|
|
69
|
+
return this.enqueueIngest(messages, sessionId, options).then(() => []);
|
|
68
70
|
}
|
|
69
|
-
return this.ingestPipeline.run(messages, sessionId);
|
|
71
|
+
return this.ingestPipeline.run(messages, sessionId, options);
|
|
70
72
|
}
|
|
71
|
-
ingestNow(messages, sessionId) {
|
|
72
|
-
return this.ingestPipeline.run(messages, sessionId);
|
|
73
|
+
ingestNow(messages, sessionId, options = {}) {
|
|
74
|
+
return this.ingestPipeline.run(messages, sessionId, options);
|
|
73
75
|
}
|
|
74
|
-
async enqueueIngest(messages, sessionId) {
|
|
76
|
+
async enqueueIngest(messages, sessionId, options = {}) {
|
|
75
77
|
if (this.ingestQueue) {
|
|
76
|
-
await this.ingestQueue.enqueue(messages, sessionId);
|
|
78
|
+
await this.ingestQueue.enqueue(messages, sessionId, options);
|
|
77
79
|
return;
|
|
78
80
|
}
|
|
79
|
-
await this.ingestPipeline.run(messages, sessionId);
|
|
81
|
+
await this.ingestPipeline.run(messages, sessionId, options);
|
|
80
82
|
}
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
+
ingestText(text, sessionId, options = {}) {
|
|
84
|
+
const pipelineOptions = this.toTextPipelineOptions(options);
|
|
85
|
+
if (this.ingestQueue) {
|
|
86
|
+
return this.enqueueTextIngest(text, sessionId, options).then(() => []);
|
|
87
|
+
}
|
|
88
|
+
return this.ingestPipeline.runText(text, sessionId, pipelineOptions);
|
|
89
|
+
}
|
|
90
|
+
async enqueueTextIngest(text, sessionId, options = {}) {
|
|
91
|
+
const pipelineOptions = this.toTextPipelineOptions(options);
|
|
92
|
+
if (this.ingestQueue) {
|
|
93
|
+
await this.ingestQueue.enqueueText(text, sessionId, pipelineOptions);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
await this.ingestPipeline.runText(text, sessionId, pipelineOptions);
|
|
97
|
+
}
|
|
98
|
+
async getTopics(sessionId, options = {}) {
|
|
99
|
+
const nodes = await this.store.getNodesBySession(sessionId, options);
|
|
83
100
|
const segments = await this.store.getSegmentsBySession(sessionId);
|
|
84
101
|
return { nodes, segments };
|
|
85
102
|
}
|
|
@@ -128,5 +145,14 @@ export class MemoGrafter {
|
|
|
128
145
|
throw new Error("MemoGrafter requires a Node.js server environment and cannot run in the browser.");
|
|
129
146
|
}
|
|
130
147
|
}
|
|
148
|
+
toTextPipelineOptions(options) {
|
|
149
|
+
return {
|
|
150
|
+
...(options.replace ? { replace: true } : {}),
|
|
151
|
+
...(options.label ? { label: options.label } : {}),
|
|
152
|
+
...(options.source ? { source: options.source } : {}),
|
|
153
|
+
...(options.tags ? { tags: options.tags } : {}),
|
|
154
|
+
sourceType: "document",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
131
157
|
}
|
|
132
158
|
//# sourceMappingURL=MemoGrafter.js.map
|
package/dist/MemoGrafter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoGrafter.js","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,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;
|
|
1
|
+
{"version":3,"file":"MemoGrafter.js","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
import type { AbsorbFromAgentOptions, GraftRegistryEntry, GraphSnapshot, InjectionResult, MemoGrafterConfig, Message, RetrievalResult, RetrieverConfig, TopicNode, TopicSegment } from "./types.js";
|
|
1
|
+
import type { AbsorbFromAgentOptions, GraftRegistryEntry, GraphSnapshot, InjectionResult, IngestTextOptions, MemoGrafterConfig, Message, RetrievalResult, RetrieverConfig, TagFilterOptions, TopicNode, TopicSegment } from "./types.js";
|
|
2
2
|
export declare class MemoGrafterAgent {
|
|
3
3
|
private readonly core;
|
|
4
4
|
private readonly sessionId;
|
|
5
5
|
private readonly history;
|
|
6
|
+
private readonly ingestionHistory;
|
|
6
7
|
private readonly baseSystemPrompt;
|
|
7
8
|
private readonly recentWindowSize;
|
|
8
9
|
private readonly recallLimit;
|
|
9
10
|
private readonly recallMinSimilarity;
|
|
10
11
|
private readonly cacheConfig;
|
|
12
|
+
private sessionTags;
|
|
11
13
|
private pendingIngest;
|
|
12
14
|
constructor(config: MemoGrafterConfig);
|
|
13
15
|
initialize(): Promise<void>;
|
|
14
16
|
invoke(userMessage: string): Promise<string>;
|
|
15
17
|
getHistory(): Message[];
|
|
16
18
|
getSessionId(): string;
|
|
17
|
-
|
|
19
|
+
ingestText(text: string, options?: IngestTextOptions): Promise<void>;
|
|
20
|
+
getActiveNodes(options?: TagFilterOptions): Promise<TopicNode[]>;
|
|
18
21
|
getActiveSegments(): Promise<TopicSegment[]>;
|
|
19
22
|
getGraphSnapshot(): Promise<GraphSnapshot>;
|
|
23
|
+
setSessionTags(tags: string[]): Promise<void>;
|
|
24
|
+
getSessionTags(): string[];
|
|
20
25
|
getGraftRegistry(): Promise<GraftRegistryEntry[]>;
|
|
21
26
|
removeGraft(nodeId: string): Promise<void>;
|
|
22
27
|
clearSession(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoGrafterAgent.d.ts","sourceRoot":"","sources":["../src/MemoGrafterAgent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,eAAe,EACf,eAAe,EACf,SAAS,EACT,YAAY,EACb,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"MemoGrafterAgent.d.ts","sourceRoot":"","sources":["../src/MemoGrafterAgent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,OAAO,EACP,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,YAAY,EACb,MAAM,YAAY,CAAC;AAIpB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAiB;IAClD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,aAAa,CAAoC;gBAE7C,MAAM,EAAE,iBAAiB;IASrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsBlD,UAAU,IAAI,OAAO,EAAE;IAIvB,YAAY,IAAI,MAAM;IAItB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBlE,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMpE,iBAAiB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAM5C,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC;IAmC1C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnD,cAAc,IAAI,MAAM,EAAE;IAIpB,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAKjD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1C,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAO7B,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAO1D,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAItD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAkB9E,eAAe,CAAC,WAAW,EAAE,gBAAgB,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAKhH,OAAO,CAAC,uBAAuB;YAUjB,mBAAmB;IAsBjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAGvB"}
|
package/dist/MemoGrafterAgent.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { MemoGrafter } from "./MemoGrafter.js";
|
|
3
3
|
import { RetrieverPipeline } from "./pipeline/RetrieverPipeline.js";
|
|
4
|
+
import { normalizeTags } from "./utils/tags.js";
|
|
5
|
+
import { splitTextForIngestion } from "./utils/text/splitTextForIngestion.js";
|
|
4
6
|
export class MemoGrafterAgent {
|
|
5
7
|
core;
|
|
6
8
|
sessionId = randomUUID();
|
|
7
9
|
history = [];
|
|
10
|
+
ingestionHistory = [];
|
|
8
11
|
baseSystemPrompt;
|
|
9
12
|
recentWindowSize;
|
|
10
13
|
recallLimit;
|
|
11
14
|
recallMinSimilarity;
|
|
12
15
|
cacheConfig;
|
|
16
|
+
sessionTags = [];
|
|
13
17
|
pendingIngest = Promise.resolve();
|
|
14
18
|
constructor(config) {
|
|
15
19
|
this.core = new MemoGrafter(config);
|
|
@@ -36,6 +40,8 @@ export class MemoGrafterAgent {
|
|
|
36
40
|
const response = await this.core.llm.complete(messages, this.baseSystemPrompt);
|
|
37
41
|
this.history.push({ role: "user", content: userMessage });
|
|
38
42
|
this.history.push({ role: "assistant", content: response });
|
|
43
|
+
this.ingestionHistory.push({ role: "user", content: userMessage });
|
|
44
|
+
this.ingestionHistory.push({ role: "assistant", content: response });
|
|
39
45
|
this.enqueueBackgroundIngest();
|
|
40
46
|
return response;
|
|
41
47
|
}
|
|
@@ -45,9 +51,27 @@ export class MemoGrafterAgent {
|
|
|
45
51
|
getSessionId() {
|
|
46
52
|
return this.sessionId;
|
|
47
53
|
}
|
|
48
|
-
|
|
54
|
+
ingestText(text, options = {}) {
|
|
55
|
+
const chunks = splitTextForIngestion(text);
|
|
56
|
+
if (chunks.length === 0)
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
const run = async () => {
|
|
59
|
+
if (options.replace) {
|
|
60
|
+
this.ingestionHistory.splice(0, this.ingestionHistory.length);
|
|
61
|
+
}
|
|
62
|
+
await this.core.enqueueTextIngest(text, this.sessionId, {
|
|
63
|
+
...options,
|
|
64
|
+
tags: this.sessionTags,
|
|
65
|
+
});
|
|
66
|
+
this.ingestionHistory.push(...chunks.map((content) => ({ role: "user", content })));
|
|
67
|
+
};
|
|
68
|
+
const operation = this.pendingIngest.then(run);
|
|
69
|
+
this.pendingIngest = operation.catch(() => undefined);
|
|
70
|
+
return operation;
|
|
71
|
+
}
|
|
72
|
+
async getActiveNodes(options = {}) {
|
|
49
73
|
await this.pendingIngest;
|
|
50
|
-
const { nodes } = await this.core.getTopics(this.sessionId);
|
|
74
|
+
const { nodes } = await this.core.getTopics(this.sessionId, options);
|
|
51
75
|
return nodes;
|
|
52
76
|
}
|
|
53
77
|
async getActiveSegments() {
|
|
@@ -87,6 +111,14 @@ export class MemoGrafterAgent {
|
|
|
87
111
|
capturedAt: new Date().toISOString(),
|
|
88
112
|
};
|
|
89
113
|
}
|
|
114
|
+
async setSessionTags(tags) {
|
|
115
|
+
await this.pendingIngest;
|
|
116
|
+
this.sessionTags = normalizeTags(tags);
|
|
117
|
+
await this.core.store.setSessionTags(this.sessionId, this.sessionTags);
|
|
118
|
+
}
|
|
119
|
+
getSessionTags() {
|
|
120
|
+
return [...this.sessionTags];
|
|
121
|
+
}
|
|
90
122
|
async getGraftRegistry() {
|
|
91
123
|
await this.pendingIngest;
|
|
92
124
|
return this.core.store.getGraftRegistry(this.sessionId);
|
|
@@ -104,6 +136,7 @@ export class MemoGrafterAgent {
|
|
|
104
136
|
await this.pendingIngest;
|
|
105
137
|
await this.core.store.clearSession(this.sessionId);
|
|
106
138
|
this.history.splice(0, this.history.length);
|
|
139
|
+
this.ingestionHistory.splice(0, this.ingestionHistory.length);
|
|
107
140
|
}
|
|
108
141
|
async graft(topicIds) {
|
|
109
142
|
await this.pendingIngest;
|
|
@@ -131,9 +164,9 @@ export class MemoGrafterAgent {
|
|
|
131
164
|
return this.core.absorbNodes(nodes, this.sessionId);
|
|
132
165
|
}
|
|
133
166
|
enqueueBackgroundIngest() {
|
|
134
|
-
const historySnapshot = [...this.
|
|
167
|
+
const historySnapshot = [...this.ingestionHistory];
|
|
135
168
|
this.pendingIngest = this.pendingIngest
|
|
136
|
-
.then(() => this.core.enqueueIngest(historySnapshot, this.sessionId))
|
|
169
|
+
.then(() => this.core.enqueueIngest(historySnapshot, this.sessionId, { tags: this.sessionTags }))
|
|
137
170
|
.catch((error) => {
|
|
138
171
|
console.warn("MemoGrafter background ingest warning:", error);
|
|
139
172
|
});
|