cozo-memory 1.0.9 โ 1.1.1
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 +36 -7
- package/dist/cli-commands.js +32 -0
- package/dist/cli.js +125 -0
- package/dist/compare-embeddings.js +402 -0
- package/dist/download-pplx-embed.js +151 -0
- package/dist/embedding-service.js +79 -7
- package/dist/eval-suite.js +7 -1
- package/dist/hybrid-search.js +87 -8
- package/dist/index.js +259 -18
- package/dist/reranker-service.js +125 -0
- package/dist/test-multi-level-memory.js +80 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -63,6 +63,10 @@ Now you can add the server to your MCP client (e.g. Claude Desktop).
|
|
|
63
63
|
|
|
64
64
|
๐ง **Agentic Retrieval Layer (v2.0)** - Auto-routing engine that analyzes query intent via local LLM to select the optimal search strategy (Vector, Graph, or Community)
|
|
65
65
|
|
|
66
|
+
๐ง **Multi-Level Memory (v2.0)** - Context-aware memory system with built-in session and task management
|
|
67
|
+
|
|
68
|
+
๐ฏ **Tiny Learned Reranker (v2.0)** - Integrated Cross-Encoder model (`ms-marco-MiniLM-L-6-v2`) for ultra-precise re-ranking of top search results
|
|
69
|
+
|
|
66
70
|
๐ฏ **Multi-Vector Support (since v1.7)** - Dual embeddings per entity: content-embedding for context, name-embedding for identification
|
|
67
71
|
|
|
68
72
|
โก **Semantic Caching (since v0.8.5)** - Two-level cache (L1 memory + L2 persistent) with semantic query matching
|
|
@@ -75,7 +79,7 @@ Now you can add the server to your MCP client (e.g. Claude Desktop).
|
|
|
75
79
|
|
|
76
80
|
๐๏ธ **Hierarchical GraphRAG (v2.0)** - Automatic generation of thematic "Community Summaries" using local LLMs to enable global "Big Picture" reasoning
|
|
77
81
|
|
|
78
|
-
๐งน **Janitor Service** - LLM-backed automatic cleanup with hierarchical summarization and
|
|
82
|
+
๐งน **Janitor Service** - LLM-backed automatic cleanup with hierarchical summarization, observation pruning, and **automated session compression**
|
|
79
83
|
|
|
80
84
|
๐ค **User Preference Profiling** - Persistent user preferences with automatic 50% search boost
|
|
81
85
|
|
|
@@ -191,8 +195,10 @@ This tool compares strategies using a synthetic dataset and measures **Recall@K*
|
|
|
191
195
|
| Method | Recall@10 | Avg Latency | Best For |
|
|
192
196
|
| :--- | :--- | :--- | :--- |
|
|
193
197
|
| **Graph-RAG** | **1.00** | **~32 ms** | Deep relational reasoning |
|
|
198
|
+
| **Graph-RAG (Reranked)** | **1.00** | **~36 ms** | Maximum precision for relational data |
|
|
194
199
|
| **Graph-Walking** | 1.00 | ~50 ms | Associative path exploration |
|
|
195
200
|
| **Hybrid Search** | 1.00 | ~89 ms | Broad factual retrieval |
|
|
201
|
+
| **Reranked Search** | 1.00 | ~20 ms* | Ultra-precise factual search (Warm cache) |
|
|
196
202
|
|
|
197
203
|
## Architecture
|
|
198
204
|
|
|
@@ -364,6 +370,7 @@ CozoDB Memory includes a full-featured CLI for all operations:
|
|
|
364
370
|
# System operations
|
|
365
371
|
cozo-memory system health
|
|
366
372
|
cozo-memory system metrics
|
|
373
|
+
cozo-memory system reflect
|
|
367
374
|
|
|
368
375
|
# Entity operations
|
|
369
376
|
cozo-memory entity create -n "MyEntity" -t "person" -m '{"age": 30}'
|
|
@@ -379,11 +386,19 @@ cozo-memory relation create --from <id1> --to <id2> --type "knows" -s 0.8
|
|
|
379
386
|
# Search
|
|
380
387
|
cozo-memory search query -q "search term" -l 10
|
|
381
388
|
cozo-memory search context -q "context query"
|
|
389
|
+
cozo-memory search agentic -q "agentic query"
|
|
382
390
|
|
|
383
391
|
# Graph operations
|
|
384
392
|
cozo-memory graph explore -s <entity-id> -h 3
|
|
385
393
|
cozo-memory graph pagerank
|
|
386
394
|
cozo-memory graph communities
|
|
395
|
+
cozo-memory graph summarize
|
|
396
|
+
|
|
397
|
+
# Session & Task management
|
|
398
|
+
cozo-memory session start -n "My Session"
|
|
399
|
+
cozo-memory session stop -i <session-id>
|
|
400
|
+
cozo-memory task start -n "My Task" -s <session-id>
|
|
401
|
+
cozo-memory task stop -i <task-id>
|
|
387
402
|
|
|
388
403
|
# Export/Import
|
|
389
404
|
cozo-memory export json -o backup.json --include-metadata --include-relationships --include-observations
|
|
@@ -508,8 +523,8 @@ The interface is reduced to **4 consolidated tools**. The concrete operation is
|
|
|
508
523
|
|
|
509
524
|
| Tool | Purpose | Key Actions |
|
|
510
525
|
|------|---------|-------------|
|
|
511
|
-
| `mutate_memory` | Write operations | create_entity, update_entity, delete_entity, add_observation, create_relation, run_transaction, add_inference_rule, ingest_file |
|
|
512
|
-
| `query_memory` | Read operations | search, advancedSearch, context, entity_details, history, graph_rag, graph_walking, agentic_search |
|
|
526
|
+
| `mutate_memory` | Write operations | create_entity, update_entity, delete_entity, add_observation, create_relation, start_session, stop_session, start_task, stop_task, run_transaction, add_inference_rule, ingest_file |
|
|
527
|
+
| `query_memory` | Read operations | search, advancedSearch, context, entity_details, history, graph_rag, graph_walking, agentic_search (Multi-Level Context support) |
|
|
513
528
|
| `analyze_graph` | Graph analysis | explore, communities, pagerank, betweenness, hits, shortest_path, bridge_discovery, semantic_walk, infer_relations |
|
|
514
529
|
| `manage_system` | Maintenance | health, metrics, export_memory, import_memory, snapshot_create, snapshot_list, snapshot_diff, cleanup, reflect, summarize_communities, clear_memory |
|
|
515
530
|
|
|
@@ -521,6 +536,10 @@ Actions:
|
|
|
521
536
|
- `delete_entity`: `{ entity_id }`
|
|
522
537
|
- `add_observation`: `{ entity_id?, entity_name?, entity_type?, text, metadata? }`
|
|
523
538
|
- `create_relation`: `{ from_id, to_id, relation_type, strength?, metadata? }`
|
|
539
|
+
- `start_session`: `{ name?, metadata? }` **(New v2.0)**: Starts a new session context (metadata can include `user_id`, `project`, etc.)
|
|
540
|
+
- `stop_session`: `{ session_id }` **(New v2.0)**: Closes/archives an active session.
|
|
541
|
+
- `start_task`: `{ name, session_id?, metadata? }` **(New v2.0)**: Starts a specific task within a session.
|
|
542
|
+
- `stop_task`: `{ task_id }` **(New v2.0)**: Marks a task as completed.
|
|
524
543
|
- `run_transaction`: `{ operations: Array<{ action, params }> }` **(New v1.2)**: Executes multiple operations atomically.
|
|
525
544
|
- `add_inference_rule`: `{ name, datalog }`
|
|
526
545
|
- `ingest_file`: `{ format, file_path?, content?, entity_id?, entity_name?, entity_type?, chunking?, metadata?, observation_metadata?, deduplicate?, max_observations? }`
|
|
@@ -608,14 +627,14 @@ PDF Ingestion via File Path:
|
|
|
608
627
|
### query_memory (Read)
|
|
609
628
|
|
|
610
629
|
Actions:
|
|
611
|
-
- `search`: `{ query, limit?, entity_types?, include_entities?, include_observations? }`
|
|
612
|
-
- `advancedSearch`: `{ query, limit?, filters?, graphConstraints?, vectorOptions? }`
|
|
630
|
+
- `search`: `{ query, limit?, entity_types?, include_entities?, include_observations?, rerank? }`
|
|
631
|
+
- `advancedSearch`: `{ query, limit?, filters?, graphConstraints?, vectorOptions?, rerank? }`
|
|
613
632
|
- `context`: `{ query, context_window?, time_range_hours? }`
|
|
614
633
|
- `entity_details`: `{ entity_id, as_of? }`
|
|
615
634
|
- `history`: `{ entity_id }`
|
|
616
|
-
- `graph_rag`: `{ query, max_depth?, limit?, filters? }` Graph-based reasoning. Finds vector seeds (with inline filtering) first and then expands transitive relationships. Uses recursive Datalog for efficient BFS expansion.
|
|
635
|
+
- `graph_rag`: `{ query, max_depth?, limit?, filters?, rerank? }` Graph-based reasoning. Finds vector seeds (with inline filtering) first and then expands transitive relationships. Uses recursive Datalog for efficient BFS expansion.
|
|
617
636
|
- `graph_walking`: `{ query, start_entity_id?, max_depth?, limit? }` (v1.7) Recursive semantic graph search. Starts at vector seeds or a specific entity and follows relationships to other semantically relevant entities. Ideal for deeper path exploration.
|
|
618
|
-
- `agentic_search`: `{ query, limit? }` **(New v2.0)**: **Auto-Routing Search**. Uses a local LLM (Ollama) to analyze query intent and automatically routes it to the most appropriate strategy (`vector_search`, `graph_walk`, or `community_summary`).
|
|
637
|
+
- `agentic_search`: `{ query, limit?, rerank? }` **(New v2.0)**: **Auto-Routing Search**. Uses a local LLM (Ollama) to analyze query intent and automatically routes it to the most appropriate strategy (`vector_search`, `graph_walk`, or `community_summary`).
|
|
619
638
|
- `get_relation_evolution`: `{ from_id, to_id?, since?, until? }` (in `analyze_graph`) Shows temporal development of relationships including time range filter and diff summary.
|
|
620
639
|
|
|
621
640
|
Important Details:
|
|
@@ -717,6 +736,7 @@ Janitor Cleanup Details:
|
|
|
717
736
|
- `cleanup` supports `dry_run`: with `confirm: false` only candidates are listed.
|
|
718
737
|
- With `confirm: true`, the Janitor becomes active:
|
|
719
738
|
- **Hierarchical Summarization**: Detects isolated or old observations, has them summarized by a local LLM (Ollama), and creates a new `ExecutiveSummary` node. Old fragments are deleted to reduce noise while preserving knowledge.
|
|
739
|
+
- **Automated Session Compression**: Automatically identifies inactive sessions, summarizes their activity into a few bullet points, and stores the summary in the User Profile while marking the session as archived.
|
|
720
740
|
|
|
721
741
|
**Before Janitor:**
|
|
722
742
|
```
|
|
@@ -885,6 +905,15 @@ Uncertainty/Transparency:
|
|
|
885
905
|
- Inference candidates are marked as `source: "inference"` and provide a short reason (uncertainty hint) in the result.
|
|
886
906
|
- In `context` output, inferred entities additionally carry an `uncertainty_hint` so an LLM can distinguish "hard fact" vs. "conjecture".
|
|
887
907
|
|
|
908
|
+
### Tiny Learned Reranker (Cross-Encoder)
|
|
909
|
+
|
|
910
|
+
For maximum precision, CozoDB Memory integrates a specialized **Cross-Encoder Reranker** (Phase 2 RAG).
|
|
911
|
+
|
|
912
|
+
- **Model**: `Xenova/ms-marco-MiniLM-L-6-v2` (Local ONNX)
|
|
913
|
+
- **Mechanism**: After initial hybrid retrieval, the top candidates (up to 30) are re-evaluated by the cross-encoder. Unlike bi-encoders (vectors), cross-encoders process query and document simultaneously, capturing deep semantic nuances.
|
|
914
|
+
- **Latency**: Minimal overhead (~4-6ms for top 10 candidates).
|
|
915
|
+
- **Supported Tools**: Available as a `rerank: true` parameter in `search`, `advancedSearch`, `graph_rag`, and `agentic_search`.
|
|
916
|
+
|
|
888
917
|
### Inference Engine
|
|
889
918
|
|
|
890
919
|
Inference uses multiple strategies (non-persisting):
|
package/dist/cli-commands.js
CHANGED
|
@@ -110,6 +110,12 @@ class CLICommands {
|
|
|
110
110
|
async advancedSearch(params) {
|
|
111
111
|
return await this.server.advancedSearch(params);
|
|
112
112
|
}
|
|
113
|
+
async agenticSearch(query, limit) {
|
|
114
|
+
return await this.server.hybridSearch.agenticRetrieve({
|
|
115
|
+
query,
|
|
116
|
+
limit: limit || 10
|
|
117
|
+
});
|
|
118
|
+
}
|
|
113
119
|
async context(query, contextWindow, timeRangeHours) {
|
|
114
120
|
// Use advancedSearch with appropriate parameters
|
|
115
121
|
return await this.server.advancedSearch({
|
|
@@ -146,6 +152,12 @@ class CLICommands {
|
|
|
146
152
|
async communities() {
|
|
147
153
|
return await this.server.recomputeCommunities();
|
|
148
154
|
}
|
|
155
|
+
async summarizeCommunities(model, minCommunitySize) {
|
|
156
|
+
return await this.server.summarizeCommunities({
|
|
157
|
+
model,
|
|
158
|
+
min_community_size: minCommunitySize
|
|
159
|
+
});
|
|
160
|
+
}
|
|
149
161
|
// System operations
|
|
150
162
|
async health() {
|
|
151
163
|
const entityCount = await this.server.db.run('?[count(id)] := *entity{id, @ "NOW"}');
|
|
@@ -162,6 +174,13 @@ class CLICommands {
|
|
|
162
174
|
// Access private metrics via type assertion
|
|
163
175
|
return this.server.metrics;
|
|
164
176
|
}
|
|
177
|
+
async reflect(entityId, model, mode) {
|
|
178
|
+
return await this.server.reflectMemory({
|
|
179
|
+
entity_id: entityId,
|
|
180
|
+
model,
|
|
181
|
+
mode
|
|
182
|
+
});
|
|
183
|
+
}
|
|
165
184
|
async exportMemory(format, options) {
|
|
166
185
|
const { ExportImportService } = await import('./export-import-service.js');
|
|
167
186
|
// Create a simple wrapper that implements DbService interface
|
|
@@ -206,5 +225,18 @@ class CLICommands {
|
|
|
206
225
|
async getUserProfile() {
|
|
207
226
|
return await this.server.editUserProfile({});
|
|
208
227
|
}
|
|
228
|
+
// Session and Task management
|
|
229
|
+
async startSession(name, metadata) {
|
|
230
|
+
return await this.server.startSession({ name, metadata });
|
|
231
|
+
}
|
|
232
|
+
async stopSession(id) {
|
|
233
|
+
return await this.server.stopSession({ id });
|
|
234
|
+
}
|
|
235
|
+
async startTask(name, sessionId, metadata) {
|
|
236
|
+
return await this.server.startTask({ name, session_id: sessionId, metadata });
|
|
237
|
+
}
|
|
238
|
+
async stopTask(id) {
|
|
239
|
+
return await this.server.stopTask({ id });
|
|
240
|
+
}
|
|
209
241
|
}
|
|
210
242
|
exports.CLICommands = CLICommands;
|
package/dist/cli.js
CHANGED
|
@@ -203,6 +203,23 @@ search
|
|
|
203
203
|
handleError(error);
|
|
204
204
|
}
|
|
205
205
|
});
|
|
206
|
+
search
|
|
207
|
+
.command('agentic')
|
|
208
|
+
.description('Perform agentic retrieval with automatic routing')
|
|
209
|
+
.requiredOption('-q, --query <query>', 'Search query')
|
|
210
|
+
.option('-l, --limit <number>', 'Result limit', parseInt, 10)
|
|
211
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
212
|
+
.action(async (options) => {
|
|
213
|
+
try {
|
|
214
|
+
await cli.init();
|
|
215
|
+
const result = await cli.agenticSearch(options.query, options.limit);
|
|
216
|
+
formatOutput(result, options.format);
|
|
217
|
+
await cli.close();
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
handleError(error);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
206
223
|
// Graph commands
|
|
207
224
|
const graph = program.command('graph').description('Graph operations');
|
|
208
225
|
graph
|
|
@@ -255,6 +272,23 @@ graph
|
|
|
255
272
|
handleError(error);
|
|
256
273
|
}
|
|
257
274
|
});
|
|
275
|
+
graph
|
|
276
|
+
.command('summarize')
|
|
277
|
+
.description('Generate hierarchical community summaries (GraphRAG)')
|
|
278
|
+
.option('-m, --model <model>', 'LLM model to use for summaries')
|
|
279
|
+
.option('--min-size <number>', 'Minimum community size', parseInt)
|
|
280
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
281
|
+
.action(async (options) => {
|
|
282
|
+
try {
|
|
283
|
+
await cli.init();
|
|
284
|
+
const result = await cli.summarizeCommunities(options.model, options.minSize);
|
|
285
|
+
formatOutput(result, options.format);
|
|
286
|
+
await cli.close();
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
handleError(error);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
258
292
|
// System commands
|
|
259
293
|
const system = program.command('system').alias('sys').description('System operations');
|
|
260
294
|
system
|
|
@@ -287,6 +321,24 @@ system
|
|
|
287
321
|
handleError(error);
|
|
288
322
|
}
|
|
289
323
|
});
|
|
324
|
+
system
|
|
325
|
+
.command('reflect')
|
|
326
|
+
.description('Perform self-reflection to discover implicit relations')
|
|
327
|
+
.option('-i, --id <id>', 'Entity ID to reflect on (optional)')
|
|
328
|
+
.option('-m, --model <model>', 'LLM model to use')
|
|
329
|
+
.option('--mode <mode>', 'Reflection mode (summary|discovery)', 'discovery')
|
|
330
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
331
|
+
.action(async (options) => {
|
|
332
|
+
try {
|
|
333
|
+
await cli.init();
|
|
334
|
+
const result = await cli.reflect(options.id, options.model, options.mode);
|
|
335
|
+
formatOutput(result, options.format);
|
|
336
|
+
await cli.close();
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
handleError(error);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
290
342
|
// Export/Import commands
|
|
291
343
|
const exportCmd = program.command('export').description('Export memory');
|
|
292
344
|
exportCmd
|
|
@@ -487,4 +539,77 @@ profile
|
|
|
487
539
|
handleError(error);
|
|
488
540
|
}
|
|
489
541
|
});
|
|
542
|
+
// Session commands
|
|
543
|
+
const session = program.command('session').description('Session management');
|
|
544
|
+
session
|
|
545
|
+
.command('start')
|
|
546
|
+
.description('Start a new session')
|
|
547
|
+
.option('-n, --name <name>', 'Session name')
|
|
548
|
+
.option('-m, --metadata <json>', 'Metadata as JSON string')
|
|
549
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
550
|
+
.action(async (options) => {
|
|
551
|
+
try {
|
|
552
|
+
await cli.init();
|
|
553
|
+
const metadata = options.metadata ? JSON.parse(options.metadata) : undefined;
|
|
554
|
+
const result = await cli.startSession(options.name, metadata);
|
|
555
|
+
formatOutput(result, options.format);
|
|
556
|
+
await cli.close();
|
|
557
|
+
}
|
|
558
|
+
catch (error) {
|
|
559
|
+
handleError(error);
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
session
|
|
563
|
+
.command('stop')
|
|
564
|
+
.description('Stop a session')
|
|
565
|
+
.requiredOption('-i, --id <id>', 'Session ID')
|
|
566
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
567
|
+
.action(async (options) => {
|
|
568
|
+
try {
|
|
569
|
+
await cli.init();
|
|
570
|
+
const result = await cli.stopSession(options.id);
|
|
571
|
+
formatOutput(result, options.format);
|
|
572
|
+
await cli.close();
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
handleError(error);
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
// Task commands
|
|
579
|
+
const task = program.command('task').description('Task management');
|
|
580
|
+
task
|
|
581
|
+
.command('start')
|
|
582
|
+
.description('Start a new task')
|
|
583
|
+
.requiredOption('-n, --name <name>', 'Task name')
|
|
584
|
+
.option('-s, --session-id <id>', 'Session ID')
|
|
585
|
+
.option('-m, --metadata <json>', 'Metadata as JSON string')
|
|
586
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
587
|
+
.action(async (options) => {
|
|
588
|
+
try {
|
|
589
|
+
await cli.init();
|
|
590
|
+
const metadata = options.metadata ? JSON.parse(options.metadata) : undefined;
|
|
591
|
+
const result = await cli.startTask(options.name, options.sessionId, metadata);
|
|
592
|
+
formatOutput(result, options.format);
|
|
593
|
+
await cli.close();
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
handleError(error);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
task
|
|
600
|
+
.command('stop')
|
|
601
|
+
.description('Stop a task')
|
|
602
|
+
.requiredOption('-i, --id <id>', 'Task ID')
|
|
603
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
604
|
+
.action(async (options) => {
|
|
605
|
+
try {
|
|
606
|
+
await cli.init();
|
|
607
|
+
const result = await cli.stopTask(options.id);
|
|
608
|
+
formatOutput(result, options.format);
|
|
609
|
+
await cli.close();
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
handleError(error);
|
|
613
|
+
}
|
|
614
|
+
});
|
|
490
615
|
program.parse();
|