mnemosyne-memory 2.1__tar.gz → 2.2__tar.gz
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.
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/CHANGELOG.md +28 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/PKG-INFO +30 -2
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/README.md +29 -1
- mnemosyne_memory-2.2/hermes_memory_provider/cli.py +260 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/hermes_plugin/tools.py +71 -6
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/__init__.py +1 -1
- mnemosyne_memory-2.2/mnemosyne/core/importers/__init__.py +152 -0
- mnemosyne_memory-2.2/mnemosyne/core/importers/agentic.py +391 -0
- mnemosyne_memory-2.2/mnemosyne/core/importers/base.py +245 -0
- mnemosyne_memory-2.2/mnemosyne/core/importers/cognee.py +290 -0
- mnemosyne_memory-2.2/mnemosyne/core/importers/honcho.py +283 -0
- mnemosyne_memory-2.2/mnemosyne/core/importers/letta.py +313 -0
- mnemosyne_memory-2.2/mnemosyne/core/importers/mem0.py +378 -0
- mnemosyne_memory-2.2/mnemosyne/core/importers/supermemory.py +260 -0
- mnemosyne_memory-2.2/mnemosyne/core/importers/zep.py +322 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne_memory.egg-info/PKG-INFO +30 -2
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne_memory.egg-info/SOURCES.txt +9 -0
- mnemosyne_memory-2.1/hermes_memory_provider/cli.py +0 -137
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/CONTRIBUTING.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/LICENSE +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/MANIFEST.in +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/UPDATING.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/assets/mnemosyne.jpg +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/README.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/api-reference.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/architecture.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/changelog.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/comparison.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/configuration.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/getting-started.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/hermes-integration.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/docs/llm-installation-guide.md +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/hermes_memory_provider/__init__.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/hermes_plugin/__init__.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/cli.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/__init__.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/aaak.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/banks.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/beam.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/cost_log.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/embeddings.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/entities.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/extraction.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/local_llm.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/memory.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/patterns.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/plugins.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/streaming.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/token_counter.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/core/triples.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/diagnose.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/dr/__init__.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/dr/recovery.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/install.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/mcp_server.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne/mcp_tools.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne_memory.egg-info/dependency_links.txt +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne_memory.egg-info/entry_points.txt +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne_memory.egg-info/requires.txt +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/mnemosyne_memory.egg-info/top_level.txt +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/pyproject.toml +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/setup.cfg +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/setup.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_beam.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_configurable_scoring.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_entities.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_entity_integration.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_extraction.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_extraction_integration.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_local_llm.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_mcp_server.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_memory_banks.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_mnemosyne_stats.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_multi_agent_identity.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_patterns.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_plugins.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_streaming.py +0 -0
- {mnemosyne_memory-2.1 → mnemosyne_memory-2.2}/tests/test_temporal_recall.py +0 -0
|
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Simple Versioning](https://github.com/AxDSan/mnemosyne) (MAJOR.MINOR).
|
|
7
7
|
|
|
8
|
+
## [2.2] — 2026-05-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
**Cross-Provider Importers — migrate from any memory platform**
|
|
13
|
+
- New `mnemosyne/core/importers/` module with 6 provider importers
|
|
14
|
+
- **Mem0:** SDK pagination → REST → structured export fallback chain; preserves user/agent/app scoping
|
|
15
|
+
- **Letta (MemGPT):** AgentFile `.af` format parsing (JSON/YAML/TOML); memory blocks → working_memory, messages → episodic
|
|
16
|
+
- **Zep:** users → sessions → `memory.get()` per-session iteration; messages + summaries + facts extraction
|
|
17
|
+
- **Cognee:** `get_graph_data()` nodes/edges extraction; nodes → episodic memories, edges → triples
|
|
18
|
+
- **Honcho:** peers → sessions → `context()` + messages; peer identity preserved as author_id
|
|
19
|
+
- **SuperMemory:** `documents.list()` + `search.execute()`; container tags mapped to channel_id
|
|
20
|
+
- **Agentic importer:** generates ready-to-run Python migration scripts and AI agent instructions for all 6 providers
|
|
21
|
+
|
|
22
|
+
**CLI: `hermes mnemosyne import` extended**
|
|
23
|
+
- `--from <provider>` — import directly from Mem0, Letta, Zep, etc.
|
|
24
|
+
- `--list-providers` — show all supported providers with docs links
|
|
25
|
+
- `--generate-script` — generate a migration script for any provider
|
|
26
|
+
- `--agentic` — output instructions to give your AI agent for extraction
|
|
27
|
+
- `--dry-run` — validate and transform without writing
|
|
28
|
+
|
|
29
|
+
**Plugin tool updated**
|
|
30
|
+
- `mnemosyne_import` schema extended with `provider`, `api_key`, `user_id`, `agent_id`, `dry_run`, `channel_id` params
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- README: added "Migrate from other memory providers" section with examples
|
|
35
|
+
|
|
8
36
|
## [2.1] — 2026-05-02
|
|
9
37
|
|
|
10
38
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mnemosyne-memory
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2
|
|
4
4
|
Summary: The Zero-Dependency, Sub-Millisecond AI Memory System
|
|
5
5
|
Home-page: https://github.com/AxDSan/mnemosyne
|
|
6
6
|
Author: Abdias J
|
|
@@ -50,7 +50,7 @@ Dynamic: requires-python
|
|
|
50
50
|
> Native, zero-cloud memory for AI agents. SQLite-backed. Sub-millisecond. Fully private.
|
|
51
51
|
|
|
52
52
|
[](https://python.org)
|
|
53
|
-
[](https://pypi.org/project/mnemosyne-memory/)
|
|
54
54
|
[](https://sqlite.org/codeofethics.html)
|
|
55
55
|
[](LICENSE)
|
|
56
56
|
[](https://github.com/AxDSan/mnemosyne/actions/workflows/ci.yml)
|
|
@@ -553,6 +553,34 @@ hermes mnemosyne export --output mnemosyne_backup.json
|
|
|
553
553
|
hermes mnemosyne import --input mnemosyne_backup.json
|
|
554
554
|
```
|
|
555
555
|
|
|
556
|
+
### Migrate from other memory providers
|
|
557
|
+
|
|
558
|
+
Import directly from 6 supported providers into Mnemosyne:
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
# List all supported providers
|
|
562
|
+
hermes mnemosyne import --list-providers
|
|
563
|
+
|
|
564
|
+
# Mem0 → Mnemosyne
|
|
565
|
+
hermes mnemosyne import --from mem0 --api-key sk-xxx
|
|
566
|
+
|
|
567
|
+
# Letta → Mnemosyne (offline .af file)
|
|
568
|
+
hermes mnemosyne import --from letta --agent-file-path ./agent.af
|
|
569
|
+
|
|
570
|
+
# Zep → Mnemosyne
|
|
571
|
+
hermes mnemosyne import --from zep --api-key sk-xxx --max-sessions 100
|
|
572
|
+
|
|
573
|
+
# Generate a migration script for any provider
|
|
574
|
+
hermes mnemosyne import --from mem0 --generate-script --output-script migrate.py
|
|
575
|
+
|
|
576
|
+
# Use AI agent extraction (no SDK needed)
|
|
577
|
+
hermes mnemosyne import --from zep --agentic
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**Supported providers:** Mem0, Letta (MemGPT), Zep, Cognee, Honcho, SuperMemory
|
|
581
|
+
|
|
582
|
+
All importers preserve metadata, timestamps, user/agent identity, and relationships (graph edges → triples). Use `--dry-run` to validate without writing.
|
|
583
|
+
|
|
556
584
|
---
|
|
557
585
|
|
|
558
586
|
## Environment Variables
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
> Native, zero-cloud memory for AI agents. SQLite-backed. Sub-millisecond. Fully private.
|
|
6
6
|
|
|
7
7
|
[](https://python.org)
|
|
8
|
-
[](https://pypi.org/project/mnemosyne-memory/)
|
|
9
9
|
[](https://sqlite.org/codeofethics.html)
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
[](https://github.com/AxDSan/mnemosyne/actions/workflows/ci.yml)
|
|
@@ -508,6 +508,34 @@ hermes mnemosyne export --output mnemosyne_backup.json
|
|
|
508
508
|
hermes mnemosyne import --input mnemosyne_backup.json
|
|
509
509
|
```
|
|
510
510
|
|
|
511
|
+
### Migrate from other memory providers
|
|
512
|
+
|
|
513
|
+
Import directly from 6 supported providers into Mnemosyne:
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
# List all supported providers
|
|
517
|
+
hermes mnemosyne import --list-providers
|
|
518
|
+
|
|
519
|
+
# Mem0 → Mnemosyne
|
|
520
|
+
hermes mnemosyne import --from mem0 --api-key sk-xxx
|
|
521
|
+
|
|
522
|
+
# Letta → Mnemosyne (offline .af file)
|
|
523
|
+
hermes mnemosyne import --from letta --agent-file-path ./agent.af
|
|
524
|
+
|
|
525
|
+
# Zep → Mnemosyne
|
|
526
|
+
hermes mnemosyne import --from zep --api-key sk-xxx --max-sessions 100
|
|
527
|
+
|
|
528
|
+
# Generate a migration script for any provider
|
|
529
|
+
hermes mnemosyne import --from mem0 --generate-script --output-script migrate.py
|
|
530
|
+
|
|
531
|
+
# Use AI agent extraction (no SDK needed)
|
|
532
|
+
hermes mnemosyne import --from zep --agentic
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Supported providers:** Mem0, Letta (MemGPT), Zep, Cognee, Honcho, SuperMemory
|
|
536
|
+
|
|
537
|
+
All importers preserve metadata, timestamps, user/agent identity, and relationships (graph edges → triples). Use `--dry-run` to validate without writing.
|
|
538
|
+
|
|
511
539
|
---
|
|
512
540
|
|
|
513
541
|
## Environment Variables
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""CLI commands for Mnemosyne memory provider.
|
|
2
|
+
|
|
3
|
+
Available via: hermes mnemosyne <subcommand>
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
_mnemosyne_root = Path(__file__).resolve().parent.parent
|
|
13
|
+
if str(_mnemosyne_root) not in sys.path:
|
|
14
|
+
sys.path.insert(0, str(_mnemosyne_root))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_cli(subparser):
|
|
18
|
+
"""Register CLI subcommands for ``hermes mnemosyne``."""
|
|
19
|
+
mn_cmds = subparser.add_subparsers(dest="mnemosyne_cmd")
|
|
20
|
+
|
|
21
|
+
stats_cmd = mn_cmds.add_parser("stats", help="Show memory statistics")
|
|
22
|
+
stats_cmd.add_argument("--global", "-g", action="store_true", help="Show global stats across all sessions")
|
|
23
|
+
|
|
24
|
+
sleep_cmd = mn_cmds.add_parser("sleep", help="Run consolidation cycle")
|
|
25
|
+
sleep_cmd.add_argument("--all-sessions", action="store_true", help="Consolidate eligible old working memories across all sessions")
|
|
26
|
+
sleep_cmd.add_argument("--dry-run", action="store_true", help="Report what would be consolidated without writing changes")
|
|
27
|
+
mn_cmds.add_parser("version", help="Show Mnemosyne version")
|
|
28
|
+
|
|
29
|
+
inspect_cmd = mn_cmds.add_parser("inspect", help="Search memories")
|
|
30
|
+
inspect_cmd.add_argument("query", nargs="?", default="", help="Search query")
|
|
31
|
+
inspect_cmd.add_argument("--limit", type=int, default=10, help="Max results")
|
|
32
|
+
|
|
33
|
+
mn_cmds.add_parser("clear", help="Clear scratchpad")
|
|
34
|
+
|
|
35
|
+
export_cmd = mn_cmds.add_parser("export", help="Export all memories to a JSON file")
|
|
36
|
+
export_cmd.add_argument("--output", "-o", type=str, required=True, help="Output JSON file path")
|
|
37
|
+
|
|
38
|
+
import_cmd = mn_cmds.add_parser("import", help="Import memories from a JSON file or another provider")
|
|
39
|
+
import_cmd.add_argument("--input", "-i", type=str, help="Input JSON file path (for file imports)")
|
|
40
|
+
import_cmd.add_argument("--force", action="store_true", help="Overwrite existing records (file import)")
|
|
41
|
+
import_cmd.add_argument("--from", dest="from_provider", type=str, help="Provider to import from (e.g., 'mem0')")
|
|
42
|
+
import_cmd.add_argument("--api-key", type=str, help="Provider API key (or set env var)")
|
|
43
|
+
import_cmd.add_argument("--user-id", type=str, help="Filter by user ID (provider-specific)")
|
|
44
|
+
import_cmd.add_argument("--agent-id", type=str, help="Filter by agent ID (provider-specific)")
|
|
45
|
+
import_cmd.add_argument("--base-url", type=str, help="Provider base URL (for self-hosted)")
|
|
46
|
+
import_cmd.add_argument("--dry-run", action="store_true", help="Validate but don't import")
|
|
47
|
+
import_cmd.add_argument("--session-id", type=str, help="Override session for imported memories")
|
|
48
|
+
import_cmd.add_argument("--channel-id", type=str, help="Channel for imported memories")
|
|
49
|
+
import_cmd.add_argument("--list-providers", action="store_true", help="List supported import providers")
|
|
50
|
+
import_cmd.add_argument("--generate-script", action="store_true", help="Generate a migration script for the provider")
|
|
51
|
+
import_cmd.add_argument("--agentic", action="store_true", help="Generate agent migration instructions (prompt to give your AI agent)")
|
|
52
|
+
import_cmd.add_argument("--output-script", type=str, help="Save generated script to file")
|
|
53
|
+
|
|
54
|
+
subparser.set_defaults(func=mnemosyne_command)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def mnemosyne_command(args):
|
|
58
|
+
"""Dispatch ``hermes mnemosyne <subcommand>``."""
|
|
59
|
+
cmd = getattr(args, "mnemosyne_cmd", None)
|
|
60
|
+
if not cmd:
|
|
61
|
+
print("Usage: hermes mnemosyne {stats|sleep|inspect|clear}")
|
|
62
|
+
return 1
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
from mnemosyne.core.beam import BeamMemory
|
|
66
|
+
beam = BeamMemory(session_id="hermes_default")
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print(f"Error: Mnemosyne not available: {e}")
|
|
69
|
+
return 1
|
|
70
|
+
|
|
71
|
+
if cmd == "stats":
|
|
72
|
+
if getattr(args, "global", False):
|
|
73
|
+
working = beam.get_global_working_stats()
|
|
74
|
+
else:
|
|
75
|
+
working = beam.get_working_stats()
|
|
76
|
+
episodic = beam.get_episodic_stats()
|
|
77
|
+
print(json.dumps({"working": working, "episodic": episodic}, indent=2))
|
|
78
|
+
|
|
79
|
+
elif cmd == "version":
|
|
80
|
+
from mnemosyne import __version__, __author__
|
|
81
|
+
print(f"Mnemosyne {__version__} by {__author__}")
|
|
82
|
+
|
|
83
|
+
elif cmd == "sleep":
|
|
84
|
+
dry_run = bool(getattr(args, "dry_run", False))
|
|
85
|
+
if getattr(args, "all_sessions", False):
|
|
86
|
+
result = beam.sleep_all_sessions(dry_run=dry_run)
|
|
87
|
+
else:
|
|
88
|
+
result = beam.sleep(dry_run=dry_run)
|
|
89
|
+
print(json.dumps(result, indent=2))
|
|
90
|
+
|
|
91
|
+
elif cmd == "inspect":
|
|
92
|
+
query = getattr(args, "query", "") or ""
|
|
93
|
+
limit = getattr(args, "limit", 10)
|
|
94
|
+
if not query:
|
|
95
|
+
query = input("Search query: ")
|
|
96
|
+
results = beam.recall(query, top_k=limit)
|
|
97
|
+
print(f"Results for '{query}': {len(results)}")
|
|
98
|
+
for i, r in enumerate(results, 1):
|
|
99
|
+
content = r.get("content", "")[:120]
|
|
100
|
+
imp = r.get("importance", 0.0)
|
|
101
|
+
print(f" {i}. [{imp:.2f}] {content}")
|
|
102
|
+
|
|
103
|
+
elif cmd == "clear":
|
|
104
|
+
confirm = input("Clear scratchpad? This cannot be undone. [y/N]: ")
|
|
105
|
+
if confirm.lower() in ("y", "yes"):
|
|
106
|
+
beam.scratchpad_clear()
|
|
107
|
+
print("Scratchpad cleared.")
|
|
108
|
+
else:
|
|
109
|
+
print("Cancelled.")
|
|
110
|
+
|
|
111
|
+
elif cmd == "export":
|
|
112
|
+
output_path = getattr(args, "output", None)
|
|
113
|
+
if not output_path:
|
|
114
|
+
print("Usage: hermes mnemosyne export --output <path>")
|
|
115
|
+
return 1
|
|
116
|
+
try:
|
|
117
|
+
from mnemosyne.core.memory import Mnemosyne
|
|
118
|
+
mem = Mnemosyne(session_id="hermes_default")
|
|
119
|
+
result = mem.export_to_file(output_path)
|
|
120
|
+
print(f"Exported {result['working_memory_count']} working, {result['episodic_memory_count']} episodic, {result['legacy_memories_count']} legacy, {result['triples_count']} triples to {output_path}")
|
|
121
|
+
except Exception as e:
|
|
122
|
+
print(f"Export failed: {e}")
|
|
123
|
+
return 1
|
|
124
|
+
|
|
125
|
+
elif cmd == "import":
|
|
126
|
+
# --list-providers
|
|
127
|
+
if getattr(args, "list_providers", False):
|
|
128
|
+
from mnemosyne.core.importers import PROVIDERS
|
|
129
|
+
print("Supported import providers:")
|
|
130
|
+
for name, info in PROVIDERS.items():
|
|
131
|
+
print(f" {name}: {info['description']}")
|
|
132
|
+
print(f" docs: {info['docs']}")
|
|
133
|
+
print(f" env key: {info['env_key']}")
|
|
134
|
+
print(f" pip: {info['pypi_package']}")
|
|
135
|
+
return 0
|
|
136
|
+
|
|
137
|
+
# --agentic: generate instructions for user's AI agent
|
|
138
|
+
generate_script_flag = getattr(args, "generate_script", False)
|
|
139
|
+
agentic_flag = getattr(args, "agentic", False)
|
|
140
|
+
from_provider = getattr(args, "from_provider", None)
|
|
141
|
+
output_script = getattr(args, "output_script", None)
|
|
142
|
+
|
|
143
|
+
if agentic_flag and from_provider:
|
|
144
|
+
from mnemosyne.core.importers.agentic import generate_agent_instructions
|
|
145
|
+
instructions = generate_agent_instructions(from_provider)
|
|
146
|
+
if output_script:
|
|
147
|
+
Path(output_script).write_text(instructions)
|
|
148
|
+
print(f"Agent instructions saved to {output_script}")
|
|
149
|
+
else:
|
|
150
|
+
print(instructions)
|
|
151
|
+
return 0
|
|
152
|
+
|
|
153
|
+
if generate_script_flag and from_provider:
|
|
154
|
+
from mnemosyne.core.importers.agentic import generate_migration_script
|
|
155
|
+
api_key = getattr(args, "api_key", None)
|
|
156
|
+
user_id = getattr(args, "user_id", None)
|
|
157
|
+
script = generate_migration_script(
|
|
158
|
+
from_provider,
|
|
159
|
+
api_key=api_key or "",
|
|
160
|
+
user_id=user_id or "",
|
|
161
|
+
)
|
|
162
|
+
if output_script:
|
|
163
|
+
Path(output_script).write_text(script)
|
|
164
|
+
print(f"Migration script saved to {output_script}")
|
|
165
|
+
else:
|
|
166
|
+
print(script)
|
|
167
|
+
return 0
|
|
168
|
+
|
|
169
|
+
cross_provider = from_provider
|
|
170
|
+
input_path = getattr(args, "input", None)
|
|
171
|
+
dry_run = getattr(args, "dry_run", False)
|
|
172
|
+
session_id = getattr(args, "session_id", None)
|
|
173
|
+
channel_id = getattr(args, "channel_id", None)
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
from mnemosyne.core.memory import Mnemosyne
|
|
177
|
+
mem = Mnemosyne(session_id=session_id or "import_session",
|
|
178
|
+
channel_id=channel_id)
|
|
179
|
+
except Exception as e:
|
|
180
|
+
print(f"Error: Mnemosyne not available: {e}")
|
|
181
|
+
return 1
|
|
182
|
+
|
|
183
|
+
# Cross-provider import
|
|
184
|
+
if cross_provider:
|
|
185
|
+
api_key = getattr(args, "api_key", None)
|
|
186
|
+
user_id = getattr(args, "user_id", None)
|
|
187
|
+
agent_id = getattr(args, "agent_id", None)
|
|
188
|
+
base_url = getattr(args, "base_url", None)
|
|
189
|
+
|
|
190
|
+
# Try env var fallback
|
|
191
|
+
import os
|
|
192
|
+
if not api_key:
|
|
193
|
+
info = __import__("mnemosyne.core.importers", fromlist=["PROVIDERS"]).PROVIDERS
|
|
194
|
+
pk = info.get(cross_provider, {}).get("env_key", "")
|
|
195
|
+
if pk:
|
|
196
|
+
api_key = os.environ.get(pk)
|
|
197
|
+
if not api_key:
|
|
198
|
+
print(f"Error: --api-key required for {cross_provider} import. "
|
|
199
|
+
f"Or set the {cross_provider.upper()}_API_KEY env var.")
|
|
200
|
+
return 1
|
|
201
|
+
|
|
202
|
+
print(f"Importing from {cross_provider}...")
|
|
203
|
+
if dry_run:
|
|
204
|
+
print(" (dry-run mode: no memories will be written)")
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
from mnemosyne.core.importers import import_from_provider
|
|
208
|
+
result = import_from_provider(
|
|
209
|
+
cross_provider, mem,
|
|
210
|
+
api_key=api_key,
|
|
211
|
+
user_id=user_id,
|
|
212
|
+
agent_id=agent_id,
|
|
213
|
+
base_url=base_url,
|
|
214
|
+
dry_run=dry_run,
|
|
215
|
+
session_id=session_id,
|
|
216
|
+
channel_id=channel_id,
|
|
217
|
+
)
|
|
218
|
+
print(f"\nImport complete:")
|
|
219
|
+
print(f" Total found: {result.total}")
|
|
220
|
+
print(f" Imported: {result.imported}")
|
|
221
|
+
print(f" Skipped: {result.skipped}")
|
|
222
|
+
print(f" Failed: {result.failed}")
|
|
223
|
+
if result.errors:
|
|
224
|
+
print(f" Errors:")
|
|
225
|
+
for err in result.errors[:10]:
|
|
226
|
+
print(f" - {err}")
|
|
227
|
+
if len(result.errors) > 10:
|
|
228
|
+
print(f" ... and {len(result.errors) - 10} more")
|
|
229
|
+
return 0 if result.failed == 0 else 1
|
|
230
|
+
except ValueError as e:
|
|
231
|
+
print(f"Error: {e}")
|
|
232
|
+
return 1
|
|
233
|
+
except Exception as e:
|
|
234
|
+
print(f"Import failed: {e}")
|
|
235
|
+
return 1
|
|
236
|
+
|
|
237
|
+
# File import
|
|
238
|
+
force = getattr(args, "force", False)
|
|
239
|
+
if not input_path:
|
|
240
|
+
print("Usage: hermes mnemosyne import --input <path> [--force]")
|
|
241
|
+
print(" hermes mnemosyne import --from <provider> --api-key <key> [--dry-run]")
|
|
242
|
+
print(" hermes mnemosyne import --list-providers")
|
|
243
|
+
return 1
|
|
244
|
+
try:
|
|
245
|
+
stats = mem.import_from_file(input_path, force=force)
|
|
246
|
+
beam_stats = stats.get("beam", {})
|
|
247
|
+
legacy_stats = stats.get("legacy", {})
|
|
248
|
+
triples_stats = stats.get("triples", {})
|
|
249
|
+
print(f"Import complete:")
|
|
250
|
+
print(f" Working: +{beam_stats.get('working_memory', {}).get('inserted', 0)}")
|
|
251
|
+
print(f" Episodic: +{beam_stats.get('episodic_memory', {}).get('inserted', 0)}")
|
|
252
|
+
print(f" Legacy: +{legacy_stats.get('inserted', 0)}")
|
|
253
|
+
print(f" Triples: +{triples_stats.get('inserted', 0)}")
|
|
254
|
+
if force:
|
|
255
|
+
print(f" (force mode: overwrites applied)")
|
|
256
|
+
except Exception as e:
|
|
257
|
+
print(f"Import failed: {e}")
|
|
258
|
+
return 1
|
|
259
|
+
|
|
260
|
+
return 0
|
|
@@ -277,13 +277,42 @@ FORGET_SCHEMA = {
|
|
|
277
277
|
|
|
278
278
|
IMPORT_SCHEMA = {
|
|
279
279
|
"name": "mnemosyne_import",
|
|
280
|
-
"description": "Import Mnemosyne memories from a JSON file. Idempotent by default.",
|
|
280
|
+
"description": "Import Mnemosyne memories from a JSON file or another memory provider (Mem0, etc.). Idempotent by default.",
|
|
281
281
|
"parameters": {
|
|
282
282
|
"type": "object",
|
|
283
283
|
"properties": {
|
|
284
284
|
"input_path": {
|
|
285
285
|
"type": "string",
|
|
286
|
-
"description": "File path to read the export JSON from"
|
|
286
|
+
"description": "File path to read the export JSON from (for file imports)"
|
|
287
|
+
},
|
|
288
|
+
"provider": {
|
|
289
|
+
"type": "string",
|
|
290
|
+
"description": "Provider to import from: 'mem0'. Requires api_key. Use --list-providers to see supported providers."
|
|
291
|
+
},
|
|
292
|
+
"api_key": {
|
|
293
|
+
"type": "string",
|
|
294
|
+
"description": "API key for the source provider (can also be set via env var: MEM0_API_KEY)"
|
|
295
|
+
},
|
|
296
|
+
"user_id": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"description": "Filter imported memories by user ID (provider-specific)"
|
|
299
|
+
},
|
|
300
|
+
"agent_id": {
|
|
301
|
+
"type": "string",
|
|
302
|
+
"description": "Filter imported memories by agent ID (provider-specific)"
|
|
303
|
+
},
|
|
304
|
+
"base_url": {
|
|
305
|
+
"type": "string",
|
|
306
|
+
"description": "Base URL for self-hosted provider instances"
|
|
307
|
+
},
|
|
308
|
+
"dry_run": {
|
|
309
|
+
"type": "boolean",
|
|
310
|
+
"description": "If true, validate and transform but don't write any memories",
|
|
311
|
+
"default": False
|
|
312
|
+
},
|
|
313
|
+
"channel_id": {
|
|
314
|
+
"type": "string",
|
|
315
|
+
"description": "Channel to assign imported memories to"
|
|
287
316
|
},
|
|
288
317
|
"force": {
|
|
289
318
|
"type": "boolean",
|
|
@@ -291,7 +320,7 @@ IMPORT_SCHEMA = {
|
|
|
291
320
|
"default": False
|
|
292
321
|
}
|
|
293
322
|
},
|
|
294
|
-
"required": [
|
|
323
|
+
"required": []
|
|
295
324
|
}
|
|
296
325
|
}
|
|
297
326
|
|
|
@@ -538,14 +567,50 @@ def mnemosyne_forget(args: dict, **kwargs) -> str:
|
|
|
538
567
|
|
|
539
568
|
|
|
540
569
|
def mnemosyne_import(args: dict, **kwargs) -> str:
|
|
541
|
-
"""Import memories from a JSON file"""
|
|
570
|
+
"""Import memories from a JSON file or another memory provider."""
|
|
542
571
|
try:
|
|
572
|
+
provider = args.get("provider", "").strip().lower()
|
|
543
573
|
input_path = args.get("input_path", "").strip()
|
|
574
|
+
dry_run = args.get("dry_run", False)
|
|
575
|
+
channel_id = args.get("channel_id")
|
|
544
576
|
force = args.get("force", False)
|
|
545
|
-
if not input_path:
|
|
546
|
-
return json.dumps({"error": "input_path is required"})
|
|
547
577
|
|
|
548
578
|
mem = _get_memory()
|
|
579
|
+
|
|
580
|
+
# Cross-provider import
|
|
581
|
+
if provider:
|
|
582
|
+
api_key = args.get("api_key", "").strip()
|
|
583
|
+
user_id = args.get("user_id", "").strip() or None
|
|
584
|
+
agent_id = args.get("agent_id", "").strip() or None
|
|
585
|
+
base_url = args.get("base_url", "").strip() or None
|
|
586
|
+
|
|
587
|
+
if not api_key:
|
|
588
|
+
import os
|
|
589
|
+
env_key = f"{provider.upper()}_API_KEY"
|
|
590
|
+
api_key = os.environ.get(env_key, "")
|
|
591
|
+
if not api_key:
|
|
592
|
+
return json.dumps({
|
|
593
|
+
"error": f"api_key required for {provider} import. Set {provider.upper()}_API_KEY env var or pass api_key parameter."
|
|
594
|
+
})
|
|
595
|
+
|
|
596
|
+
from mnemosyne.core.importers import import_from_provider
|
|
597
|
+
result = import_from_provider(
|
|
598
|
+
provider, mem,
|
|
599
|
+
api_key=api_key,
|
|
600
|
+
user_id=user_id,
|
|
601
|
+
agent_id=agent_id,
|
|
602
|
+
base_url=base_url,
|
|
603
|
+
dry_run=dry_run,
|
|
604
|
+
channel_id=channel_id,
|
|
605
|
+
)
|
|
606
|
+
return json.dumps(result.to_dict())
|
|
607
|
+
|
|
608
|
+
# File import
|
|
609
|
+
if not input_path:
|
|
610
|
+
return json.dumps({
|
|
611
|
+
"error": "Either input_path (for file import) or provider (for cross-provider import) is required"
|
|
612
|
+
})
|
|
613
|
+
|
|
549
614
|
stats = mem.import_from_file(input_path, force=force)
|
|
550
615
|
return json.dumps({
|
|
551
616
|
"status": "imported",
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mnemosyne Cross-Provider Importers
|
|
3
|
+
|
|
4
|
+
Import memories from other AI memory providers into Mnemosyne.
|
|
5
|
+
|
|
6
|
+
Supported providers:
|
|
7
|
+
mem0 — Mem0 (cloud + self-hosted)
|
|
8
|
+
letta — Letta (formerly MemGPT)
|
|
9
|
+
zep — Zep enterprise memory
|
|
10
|
+
cognee — Cognee graph memory
|
|
11
|
+
honcho — Honcho entity memory
|
|
12
|
+
supermemory — SuperMemory cloud API
|
|
13
|
+
|
|
14
|
+
CLI usage:
|
|
15
|
+
hermes mnemosyne import --from mem0 --api-key sk-xxx
|
|
16
|
+
hermes mnemosyne import --file export.json --dry-run
|
|
17
|
+
hermes mnemosyne import --list-providers
|
|
18
|
+
hermes mnemosyne import --from mem0 --generate-script
|
|
19
|
+
|
|
20
|
+
Agentic migration:
|
|
21
|
+
hermes mnemosyne import --from zep --agentic
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from .base import BaseImporter, ImporterResult, import_from_file
|
|
25
|
+
from .mem0 import Mem0Importer, import_from_mem0
|
|
26
|
+
from .letta import LettaImporter
|
|
27
|
+
from .zep import ZepImporter
|
|
28
|
+
from .cognee import CogneeImporter
|
|
29
|
+
from .honcho import HonchoImporter
|
|
30
|
+
from .supermemory import SuperMemoryImporter
|
|
31
|
+
from .agentic import (
|
|
32
|
+
AgenticImporter,
|
|
33
|
+
generate_migration_script,
|
|
34
|
+
generate_agent_instructions,
|
|
35
|
+
generate_docs_instructions,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Registry of supported providers
|
|
40
|
+
PROVIDERS = {
|
|
41
|
+
"mem0": {
|
|
42
|
+
"name": "Mem0",
|
|
43
|
+
"class": Mem0Importer,
|
|
44
|
+
"module": "mem0",
|
|
45
|
+
"docs": "https://docs.mem0.ai",
|
|
46
|
+
"env_key": "MEM0_API_KEY",
|
|
47
|
+
"pypi_package": "mem0ai",
|
|
48
|
+
"description": "Memory platform with 24 vector store backends. Supports user/agent/app scoping.",
|
|
49
|
+
},
|
|
50
|
+
"letta": {
|
|
51
|
+
"name": "Letta (MemGPT)",
|
|
52
|
+
"class": LettaImporter,
|
|
53
|
+
"module": "letta",
|
|
54
|
+
"docs": "https://docs.letta.com",
|
|
55
|
+
"env_key": "LETTA_API_KEY",
|
|
56
|
+
"pypi_package": "letta-client",
|
|
57
|
+
"description": "Agent OS with hierarchical memory blocks. Export via .af AgentFile format.",
|
|
58
|
+
},
|
|
59
|
+
"zep": {
|
|
60
|
+
"name": "Zep",
|
|
61
|
+
"class": ZepImporter,
|
|
62
|
+
"module": "zep",
|
|
63
|
+
"docs": "https://docs.getzep.com",
|
|
64
|
+
"env_key": "ZEP_API_KEY",
|
|
65
|
+
"pypi_package": "zep-cloud",
|
|
66
|
+
"description": "Enterprise temporal knowledge graph. Session-based with user/thread model.",
|
|
67
|
+
},
|
|
68
|
+
"cognee": {
|
|
69
|
+
"name": "Cognee",
|
|
70
|
+
"class": CogneeImporter,
|
|
71
|
+
"module": "cognee",
|
|
72
|
+
"docs": "https://docs.cognee.ai",
|
|
73
|
+
"env_key": None,
|
|
74
|
+
"pypi_package": "cognee",
|
|
75
|
+
"description": "Graph-based memory with Kùzu + LanceDB + SQLite. Nodes/edges map to episodic/triples.",
|
|
76
|
+
},
|
|
77
|
+
"honcho": {
|
|
78
|
+
"name": "Honcho",
|
|
79
|
+
"class": HonchoImporter,
|
|
80
|
+
"module": "honcho",
|
|
81
|
+
"docs": "https://docs.honcho.dev",
|
|
82
|
+
"env_key": None,
|
|
83
|
+
"pypi_package": "honcho-ai",
|
|
84
|
+
"description": "Entity-centric memory by Plastic Labs. Workspace → Peer → Session → Message model.",
|
|
85
|
+
},
|
|
86
|
+
"supermemory": {
|
|
87
|
+
"name": "SuperMemory",
|
|
88
|
+
"class": SuperMemoryImporter,
|
|
89
|
+
"module": "supermemory",
|
|
90
|
+
"docs": "https://supermemory.ai/docs",
|
|
91
|
+
"env_key": "SUPERMEMORY_API_KEY",
|
|
92
|
+
"pypi_package": "supermemory",
|
|
93
|
+
"description": "Cloud memory API with container tags and document management.",
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def list_providers() -> list:
|
|
99
|
+
"""Return list of supported provider names."""
|
|
100
|
+
return list(PROVIDERS.keys())
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_provider_info(name: str) -> dict:
|
|
104
|
+
"""Get metadata for a supported provider."""
|
|
105
|
+
return PROVIDERS.get(name, {})
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def import_from_provider(provider: str, mnemosyne, dry_run: bool = False,
|
|
109
|
+
session_id: str = None, channel_id: str = None,
|
|
110
|
+
**kwargs) -> ImporterResult:
|
|
111
|
+
"""Import memories from a supported provider into Mnemosyne.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
provider: Provider name (e.g., 'mem0', 'letta').
|
|
115
|
+
mnemosyne: Mnemosyne instance to import into.
|
|
116
|
+
dry_run: If True, validate and transform but don't write.
|
|
117
|
+
session_id: Override session for imported memories.
|
|
118
|
+
channel_id: Channel to assign imported memories to.
|
|
119
|
+
**kwargs: Provider-specific arguments (api_key, user_id, etc.)
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
ImporterResult with import statistics.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
ValueError: If provider is not supported.
|
|
126
|
+
"""
|
|
127
|
+
provider_info = PROVIDERS.get(provider)
|
|
128
|
+
if not provider_info:
|
|
129
|
+
supported = ", ".join(PROVIDERS.keys())
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"Unsupported provider: '{provider}'. Supported: {supported}"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
importer_cls = provider_info["class"]
|
|
135
|
+
importer = importer_cls(**kwargs)
|
|
136
|
+
return importer.run(
|
|
137
|
+
mnemosyne,
|
|
138
|
+
dry_run=dry_run,
|
|
139
|
+
session_id=session_id,
|
|
140
|
+
channel_id=channel_id,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def generate_script(provider: str, **kwargs) -> str:
|
|
145
|
+
"""Generate a migration script for the given provider."""
|
|
146
|
+
provider_info = PROVIDERS.get(provider)
|
|
147
|
+
if not provider_info:
|
|
148
|
+
supported = ", ".join(PROVIDERS.keys())
|
|
149
|
+
raise ValueError(
|
|
150
|
+
f"Unsupported provider: '{provider}'. Supported: {supported}"
|
|
151
|
+
)
|
|
152
|
+
return generate_migration_script(provider, **kwargs)
|