memplex 3.2.4__tar.gz → 3.2.7__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.
- {memplex-3.2.4 → memplex-3.2.7}/PKG-INFO +5 -2
- memplex-3.2.7/README.md +186 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/.claude-plugin/plugin.json +1 -1
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/scripts/hook-runner.py +27 -1
- {memplex-3.2.4 → memplex-3.2.7}/memplex/adapters/agent_installer.py +13 -1
- {memplex-3.2.4 → memplex-3.2.7}/memplex/adapters/agent_runtime.py +2 -7
- {memplex-3.2.4 → memplex-3.2.7}/memplex/adapters/cli.py +5 -13
- {memplex-3.2.4 → memplex-3.2.7}/memplex/adapters/mcp_server.py +1 -1
- {memplex-3.2.4 → memplex-3.2.7}/memplex/config.py +2 -4
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/associator/term_mapper.py +8 -1
- {memplex-3.2.4 → memplex-3.2.7}/memplex/retrieval/embedding.py +191 -8
- {memplex-3.2.4 → memplex-3.2.7}/memplex/service.py +21 -2
- memplex-3.2.7/memplex/storage/lite/search_index.py +305 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/storage/lite/store.py +76 -63
- {memplex-3.2.4 → memplex-3.2.7}/memplex.egg-info/PKG-INFO +5 -2
- {memplex-3.2.4 → memplex-3.2.7}/memplex.egg-info/SOURCES.txt +3 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex.egg-info/requires.txt +5 -1
- {memplex-3.2.4 → memplex-3.2.7}/pyproject.toml +6 -2
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_agent_hot_paths.py +9 -11
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_agent_runtime.py +27 -0
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_associators.py +15 -0
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_config.py +5 -0
- memplex-3.2.7/tests/test_e2e_robustness.py +203 -0
- memplex-3.2.7/tests/test_embedding.py +187 -0
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_hooks.py +9 -2
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_install_scripts.py +3 -3
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_storage.py +144 -0
- memplex-3.2.4/README.md +0 -136
- {memplex-3.2.4 → memplex-3.2.7}/LICENSE +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/__main__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/.mcp.json +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/hooks/hooks.json +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/skills/mem-explore/SKILL.md +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/skills/mem-manage/SKILL.md +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/skills/mem-search/SKILL.md +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/_plugin/skills/mem-write/SKILL.md +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/adapters/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/adapters/claude_skill.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/adapters/http_api.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/base.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/benchmark_cli.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/evaluator.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/loader.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/locomo.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/memory_eval.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/memory_metrics.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/metrics.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/nq_trivia.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/benchmarks/popqa_hotpot.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/compaction.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/associator/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/associator/domain_classifier.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/associator/entity_aligner.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/associator/ref_linker.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/dictionaries/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/engine.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/extractors/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/extractors/docx.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/extractors/image.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/extractors/markdown.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/extractors/pdf.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/extractors/vision_mapper.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/handlers/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/handlers/clipboard.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/handlers/file_handler.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/handlers/url_handler.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/hooks/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/hooks/collector.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/hooks/hook_event.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/core/hooks/registry.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/enhancer.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/fallback_chain.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/injection_guard.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/provider.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/providers/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/providers/anthropic.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/providers/local.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/providers/rule_based.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/llm/sanitizer.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/logging_utils.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/metrics.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/feedback.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/graph.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/memory.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/misc.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/paragraph.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/search.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/source.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/models/task.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/processing/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/processing/graph_builder.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/processing/merger/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/processing/merger/confidence_calculator.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/processing/merger/conflict_resolver.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/retrieval/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/retrieval/dedup.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/retrieval/reranker.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/storage/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/storage/base.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/storage/changelog.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/storage/feedback.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/storage/lite/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/storage/vector.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/wiki/__init__.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/wiki/community.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/wiki/compiler.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/wiki/generator.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/wiki/search.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex/worker.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex.egg-info/dependency_links.txt +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex.egg-info/entry_points.txt +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/memplex.egg-info/top_level.txt +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/setup.cfg +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_core_engine.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_graph_builder.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_llm.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_models.py +0 -0
- {memplex-3.2.4 → memplex-3.2.7}/tests/test_service.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memplex
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.7
|
|
4
4
|
Summary: Memplex - Memory Complex: multi-agent knowledge graph memory system with 3-layer retrieval
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
License-File: LICENSE
|
|
@@ -17,6 +17,9 @@ Requires-Dist: pytesseract>=0.3.10; extra == "extractors"
|
|
|
17
17
|
Provides-Extra: vector
|
|
18
18
|
Requires-Dist: chromadb>=0.4.0; extra == "vector"
|
|
19
19
|
Requires-Dist: sentence-transformers>=2.0; extra == "vector"
|
|
20
|
+
Provides-Extra: local-onnx
|
|
21
|
+
Requires-Dist: onnxruntime>=1.17.0; extra == "local-onnx"
|
|
22
|
+
Requires-Dist: tokenizers>=0.15.0; extra == "local-onnx"
|
|
20
23
|
Provides-Extra: graph
|
|
21
24
|
Requires-Dist: networkx>=3.0; extra == "graph"
|
|
22
25
|
Requires-Dist: python-louvain>=0.16; extra == "graph"
|
|
@@ -31,7 +34,7 @@ Requires-Dist: asyncpg>=0.29.0; extra == "postgres"
|
|
|
31
34
|
Provides-Extra: neo4j
|
|
32
35
|
Requires-Dist: neo4j>=5.0.0; extra == "neo4j"
|
|
33
36
|
Provides-Extra: all
|
|
34
|
-
Requires-Dist: memplex[embedding,extractors,graph,http,llm,vector]; extra == "all"
|
|
37
|
+
Requires-Dist: memplex[embedding,extractors,graph,http,llm,local-onnx,vector]; extra == "all"
|
|
35
38
|
Provides-Extra: dev
|
|
36
39
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
37
40
|
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
memplex-3.2.7/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Memplex
|
|
2
|
+
|
|
3
|
+
Memplex is a shared long-term memory layer for AI agents. It gives Codex,
|
|
4
|
+
Claude Code, OpenClaw, Hermes, and similar local agents the same closed loop:
|
|
5
|
+
recall useful memory before a turn, capture what happened after the turn, and
|
|
6
|
+
compact old context in the background.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
No source checkout is required.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx memplex setup
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Install into a specific agent:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx memplex setup --agent codex --project-path "$PWD"
|
|
20
|
+
npx memplex setup --agent claude-code --project-path "$PWD"
|
|
21
|
+
npx memplex setup --agent openclaw --project-path "$PWD"
|
|
22
|
+
npx memplex setup --agent hermes --project-path "$PWD"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Install every supported local agent:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx memplex setup --agent all --project-path "$PWD"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Uninstall is symmetrical:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx memplex uninstall --agent all
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The npm wrapper creates a persistent Python environment at
|
|
38
|
+
`~/.local/share/memplex/agent-venv`, installs `memplex==3.2.7`, detects local
|
|
39
|
+
agent config directories, and registers Memplex into the selected hosts. It uses
|
|
40
|
+
`uv` when available and falls back to `python -m venv` plus `pip`.
|
|
41
|
+
|
|
42
|
+
Python-first users can skip npm:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv tool install memplex==3.2.7
|
|
46
|
+
memplex setup --agent all --project-path "$PWD"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Shell-only fallback:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
curl -fsSL https://raw.githubusercontent.com/articultur/memplex/main/scripts/install-agent.sh | bash
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## What Gets Installed
|
|
56
|
+
|
|
57
|
+
| Agent | Integration | Installed shape |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| Codex | MCP server | marker-bounded `mcp_servers.memplex` config |
|
|
60
|
+
| Claude Code | plugin + lifecycle hooks + MCP | packaged plugin and hook runner |
|
|
61
|
+
| OpenClaw | memory plugin slot | `plugins.slots.memory = "memplex"` plus extension files |
|
|
62
|
+
| Hermes | memory provider plugin | provider descriptor and `plugins/memory/memplex` |
|
|
63
|
+
|
|
64
|
+
All installers are reversible and refuse to overwrite unmanaged existing
|
|
65
|
+
Memplex entries.
|
|
66
|
+
|
|
67
|
+
## Verify
|
|
68
|
+
|
|
69
|
+
List supported profiles:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
memplex --output json agent list
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Run a local closed-loop smoke:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
memplex --output json agent capture \
|
|
79
|
+
--agent codex \
|
|
80
|
+
--user-message "Use lite storage for local development." \
|
|
81
|
+
--assistant-message "Recorded."
|
|
82
|
+
|
|
83
|
+
memplex --output json agent recall \
|
|
84
|
+
--agent codex \
|
|
85
|
+
"What storage should local development use?"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For a no-write preview:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx memplex setup --agent all --project-path "$PWD" --dry-run
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Offline And Mainland China
|
|
95
|
+
|
|
96
|
+
Memplex's default local retrieval uses a SQLite FTS5 sidecar index with
|
|
97
|
+
`bm25()` ranking plus generated trigram tokens for Chinese, code symbols,
|
|
98
|
+
paths, and short memory fragments. If SQLite FTS5 is unavailable, it falls back
|
|
99
|
+
to pure-Python local BM25/trigram matching. The agent hot path does not need
|
|
100
|
+
HuggingFace, so `npx memplex setup`, capture, recall, MCP tools, and hooks
|
|
101
|
+
continue to work when HuggingFace is blocked or unavailable.
|
|
102
|
+
|
|
103
|
+
The embedding fallback is also local. To force that path explicitly:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
MEMPLEX_EMBEDDING_MODEL=tfidf memplex query "local memory"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
To enhance semantic recall with a local model and no network downloads:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
python -m pip install "memplex[local-onnx]"
|
|
113
|
+
export MEMPLEX_LOCAL_ONNX_MODEL=/models/bge-small/model.onnx
|
|
114
|
+
export MEMPLEX_LOCAL_ONNX_TOKENIZER=/models/bge-small/tokenizer.json
|
|
115
|
+
memplex query "local semantic memory"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`MEMPLEX_EMBEDDING_MODEL=local-onnx` or
|
|
119
|
+
`MEMPLEX_EMBEDDING_MODEL=local-onnx:/models/bge-small/model.onnx` opts in
|
|
120
|
+
explicitly and reports configuration errors. With the default auto-enhancement
|
|
121
|
+
path, a missing local ONNX runtime or model keeps the SQLite
|
|
122
|
+
FTS5/BM25+trigram retrieval path alive and falls back to local embedding.
|
|
123
|
+
|
|
124
|
+
Opt into HuggingFace only when the environment can reach it or the model is
|
|
125
|
+
already cached:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
MEMPLEX_EMBEDDING_MODEL=minilm memplex query "semantic memory"
|
|
129
|
+
MEMPLEX_EMBEDDING_MODEL=bge-m3 memplex query "中文记忆"
|
|
130
|
+
MEMPLEX_EMBEDDING_MODEL=hf:BAAI/bge-m3 memplex query "中文记忆"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If your organization provides an approved HuggingFace mirror, configure it in
|
|
134
|
+
the Python/HuggingFace environment before enabling those models. Otherwise keep
|
|
135
|
+
the default local retrieval and embedding path for agent reliability.
|
|
136
|
+
|
|
137
|
+
## Core Features
|
|
138
|
+
|
|
139
|
+
- **Automatic agent loop**: pre-turn recall, post-turn capture, background
|
|
140
|
+
consolidation.
|
|
141
|
+
- **4 memory types**: Function, Fact, Preference, Observation.
|
|
142
|
+
- **3-layer retrieval**: SQLite FTS5/BM25+trigram search, timeline, get.
|
|
143
|
+
- **5-dim reranking**: raw relevance, semantic similarity, recency, source
|
|
144
|
+
authority, frequency.
|
|
145
|
+
- **5-stage compaction**: extract, dedup, summarize, prune, archive.
|
|
146
|
+
- **Wiki layer**: full-text/vector retrieval plus graph-aware synthesis.
|
|
147
|
+
- **Namespacing**: user, session, project path, and storage path isolation.
|
|
148
|
+
|
|
149
|
+
## Docs
|
|
150
|
+
|
|
151
|
+
- [Getting Started](docs/getting-started.md): install, verify, uninstall, and
|
|
152
|
+
troubleshoot.
|
|
153
|
+
- [Explainer](docs/explainer.md): what Memplex is and how the memory loop works.
|
|
154
|
+
- [Agent Integration Loop](docs/agent-integration.md): adapter contracts for
|
|
155
|
+
Codex, Claude Code, OpenClaw, and Hermes.
|
|
156
|
+
- [Release Automation](docs/release-automation.md): npm token handling and
|
|
157
|
+
automated npm publishing.
|
|
158
|
+
|
|
159
|
+
## From Source
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
git clone https://github.com/articultur/memplex.git
|
|
163
|
+
cd memplex
|
|
164
|
+
pip install -e .
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## CLI Basics
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
memplex write --text "Python list comprehensions are faster than loops"
|
|
171
|
+
memplex query "python performance"
|
|
172
|
+
memplex compact
|
|
173
|
+
memplex health
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Storage And Privacy
|
|
177
|
+
|
|
178
|
+
The default local backend is a JSON-backed LiteMemoryStore at
|
|
179
|
+
`.memplex/memory.json`. Memplex can also use vector and feedback backends when
|
|
180
|
+
configured.
|
|
181
|
+
|
|
182
|
+
Content wrapped in `<private>...</private>` is not stored.
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT
|
|
@@ -199,6 +199,31 @@ def _print_contract(content: str = "") -> None:
|
|
|
199
199
|
print(OUTPUT_CONTRACT)
|
|
200
200
|
|
|
201
201
|
|
|
202
|
+
def _package_version(memplex_module: Any) -> str:
|
|
203
|
+
"""Resolve package version, preferring source-tree pyproject metadata."""
|
|
204
|
+
try:
|
|
205
|
+
import tomllib
|
|
206
|
+
|
|
207
|
+
for parent in Path(__file__).resolve().parents:
|
|
208
|
+
pyproject = parent / "pyproject.toml"
|
|
209
|
+
if not pyproject.exists():
|
|
210
|
+
continue
|
|
211
|
+
project = tomllib.loads(pyproject.read_text(encoding="utf-8")).get(
|
|
212
|
+
"project", {}
|
|
213
|
+
)
|
|
214
|
+
if project.get("name") == "memplex" and project.get("version"):
|
|
215
|
+
return str(project["version"])
|
|
216
|
+
except Exception:
|
|
217
|
+
pass
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
from importlib.metadata import version as pkg_version
|
|
221
|
+
|
|
222
|
+
return pkg_version("memplex")
|
|
223
|
+
except Exception:
|
|
224
|
+
return getattr(memplex_module, "__version__", "unknown")
|
|
225
|
+
|
|
226
|
+
|
|
202
227
|
def cmd_setup() -> None:
|
|
203
228
|
"""Check environment on plugin install."""
|
|
204
229
|
try:
|
|
@@ -207,7 +232,7 @@ def cmd_setup() -> None:
|
|
|
207
232
|
from memplex.config import load_config
|
|
208
233
|
|
|
209
234
|
# Verify memplex is importable
|
|
210
|
-
version =
|
|
235
|
+
version = _package_version(memplex)
|
|
211
236
|
|
|
212
237
|
# Check config
|
|
213
238
|
try:
|
|
@@ -219,6 +244,7 @@ def cmd_setup() -> None:
|
|
|
219
244
|
# Initialize service if config exists
|
|
220
245
|
if cfg:
|
|
221
246
|
from memplex.service import MemplexService
|
|
247
|
+
|
|
222
248
|
service = MemplexService(config=cfg)
|
|
223
249
|
health = service.health()
|
|
224
250
|
print(f"[Memplex] v{version} installed. Status: {health.get('status', 'unknown')}")
|
|
@@ -938,9 +938,21 @@ def _marketplace_json() -> str:
|
|
|
938
938
|
|
|
939
939
|
|
|
940
940
|
def _package_version() -> str:
|
|
941
|
+
pyproject = Path(__file__).resolve().parents[2] / "pyproject.toml"
|
|
942
|
+
if pyproject.exists():
|
|
943
|
+
try:
|
|
944
|
+
import tomllib
|
|
945
|
+
|
|
946
|
+
data = tomllib.loads(pyproject.read_text(encoding="utf-8"))
|
|
947
|
+
project = data.get("project", {})
|
|
948
|
+
if project.get("name") == "memplex" and project.get("version"):
|
|
949
|
+
return str(project["version"])
|
|
950
|
+
except Exception:
|
|
951
|
+
pass
|
|
952
|
+
|
|
941
953
|
from importlib.metadata import version as pkg_version
|
|
942
954
|
|
|
943
955
|
try:
|
|
944
956
|
return pkg_version("memplex")
|
|
945
957
|
except Exception:
|
|
946
|
-
return "3.2.
|
|
958
|
+
return "3.2.7"
|
|
@@ -235,9 +235,7 @@ class AgentMemoryRuntime:
|
|
|
235
235
|
self.auto_recall = auto_recall
|
|
236
236
|
capabilities = AGENT_PROFILES[self.agent].capabilities
|
|
237
237
|
self.prefetch_enabled = (
|
|
238
|
-
capabilities.get("zero_latency_prefetch", False)
|
|
239
|
-
if prefetch is None
|
|
240
|
-
else prefetch
|
|
238
|
+
capabilities.get("zero_latency_prefetch", False) if prefetch is None else prefetch
|
|
241
239
|
)
|
|
242
240
|
self._prefetch_cache = _PREFETCH_CACHE
|
|
243
241
|
|
|
@@ -338,10 +336,7 @@ class AgentMemoryRuntime:
|
|
|
338
336
|
wrapped = IndirectInjectionGuard.filter_and_wrap(result.results, self.service.store)
|
|
339
337
|
if wrapped:
|
|
340
338
|
return wrapped
|
|
341
|
-
|
|
342
|
-
for item in result.results:
|
|
343
|
-
lines.append(f"[MEMORY id={item.func_id}] {item.summary}")
|
|
344
|
-
return "\n".join(lines)
|
|
339
|
+
return "[MEMORY FILTERED | reason=indirect_injection]\nUnsafe recalled memory was omitted."
|
|
345
340
|
|
|
346
341
|
@staticmethod
|
|
347
342
|
def _query_from_prompt(prompt: str) -> str:
|
|
@@ -268,7 +268,7 @@ def cmd_health(args: argparse.Namespace) -> int:
|
|
|
268
268
|
try:
|
|
269
269
|
info = svc.health()
|
|
270
270
|
print(_fmt(info, args.output))
|
|
271
|
-
return 0 if info.get("status")
|
|
271
|
+
return 0 if info.get("status") in {"healthy", "warning"} else 1
|
|
272
272
|
finally:
|
|
273
273
|
svc.stop()
|
|
274
274
|
|
|
@@ -477,9 +477,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
477
477
|
# -- feedback --
|
|
478
478
|
p_fb = sub.add_parser("feedback", help="Submit feedback on a memory field")
|
|
479
479
|
p_fb.add_argument("memory_id", help="Memory ID")
|
|
480
|
-
p_fb.add_argument(
|
|
481
|
-
"--role", required=True, help="Field role (trigger|action|condition|benefit)"
|
|
482
|
-
)
|
|
480
|
+
p_fb.add_argument("--role", required=True, help="Field role (trigger|action|condition|benefit)")
|
|
483
481
|
p_fb.add_argument("--index", type=int, required=True, help="Value index")
|
|
484
482
|
p_fb.add_argument(
|
|
485
483
|
"--verdict",
|
|
@@ -508,9 +506,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
508
506
|
|
|
509
507
|
# -- agent --
|
|
510
508
|
p_agent = sub.add_parser("agent", help="Portable agent integration commands")
|
|
511
|
-
agent_sub = p_agent.add_subparsers(
|
|
512
|
-
dest="agent_command", help="Agent integration command"
|
|
513
|
-
)
|
|
509
|
+
agent_sub = p_agent.add_subparsers(dest="agent_command", help="Agent integration command")
|
|
514
510
|
agent_sub.add_parser("list", help="List supported agent profiles")
|
|
515
511
|
|
|
516
512
|
p_agent_manifest = agent_sub.add_parser("manifest", help="Show agent manifest")
|
|
@@ -520,9 +516,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
520
516
|
help="Agent id: codex | claude-code | openclaw | hermes | all",
|
|
521
517
|
)
|
|
522
518
|
|
|
523
|
-
p_agent_install = agent_sub.add_parser(
|
|
524
|
-
"install", help="Install Memplex into an agent host"
|
|
525
|
-
)
|
|
519
|
+
p_agent_install = agent_sub.add_parser("install", help="Install Memplex into an agent host")
|
|
526
520
|
p_agent_install.add_argument(
|
|
527
521
|
"--agent",
|
|
528
522
|
default="all",
|
|
@@ -565,9 +559,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
565
559
|
p_agent_recall.add_argument("--top-k", type=int, default=5)
|
|
566
560
|
p_agent_recall.add_argument("--token-budget", type=int, default=1500)
|
|
567
561
|
|
|
568
|
-
p_agent_capture = agent_sub.add_parser(
|
|
569
|
-
"capture", help="Capture a completed agent turn"
|
|
570
|
-
)
|
|
562
|
+
p_agent_capture = agent_sub.add_parser("capture", help="Capture a completed agent turn")
|
|
571
563
|
p_agent_capture.add_argument("--agent", default="codex")
|
|
572
564
|
p_agent_capture.add_argument("--user-id", default=None)
|
|
573
565
|
p_agent_capture.add_argument("--session-id", default="default")
|
|
@@ -29,7 +29,7 @@ class StorageConfig:
|
|
|
29
29
|
class EmbeddingConfig:
|
|
30
30
|
"""Embedding model configuration."""
|
|
31
31
|
|
|
32
|
-
model: str = "default" # default
|
|
32
|
+
model: str = "default" # default=local, optional local ONNX; hf:<id>=HF
|
|
33
33
|
dimension: int = 384
|
|
34
34
|
batch_size: int = 32
|
|
35
35
|
contextual_retrieval: bool = True
|
|
@@ -263,9 +263,7 @@ def _apply_env_overrides(config: MemplexConfig) -> None:
|
|
|
263
263
|
# Handle LLM fallback_chain via MEMPLEX_LLM_FALLBACK_CHAIN (comma-separated)
|
|
264
264
|
fallback_env = os.environ.get("MEMPLEX_LLM_FALLBACK_CHAIN")
|
|
265
265
|
if fallback_env is not None:
|
|
266
|
-
config.llm.fallback_chain = [
|
|
267
|
-
s.strip() for s in fallback_env.split(",") if s.strip()
|
|
268
|
-
]
|
|
266
|
+
config.llm.fallback_chain = [s.strip() for s in fallback_env.split(",") if s.strip()]
|
|
269
267
|
|
|
270
268
|
|
|
271
269
|
# ── YAML loading helpers ────────────────────────────────────────
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Term-based association using dictionary lookup."""
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
from typing import List, Optional, Set, Tuple
|
|
4
5
|
|
|
5
6
|
from memplex.core.dictionaries import TermDictionary
|
|
6
7
|
from memplex.models.memory import Function
|
|
7
8
|
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
class TermMapper:
|
|
10
13
|
"""Maps terms between documents using dictionary lookup."""
|
|
@@ -13,7 +16,7 @@ class TermMapper:
|
|
|
13
16
|
self.dictionary = dictionary or TermDictionary()
|
|
14
17
|
|
|
15
18
|
def embed_text(self, text: str) -> Optional[List[float]]:
|
|
16
|
-
"""Generate embedding vector using sentence-transformers
|
|
19
|
+
"""Generate embedding vector using sentence-transformers when available."""
|
|
17
20
|
try:
|
|
18
21
|
from sentence_transformers import SentenceTransformer
|
|
19
22
|
|
|
@@ -22,6 +25,10 @@ class TermMapper:
|
|
|
22
25
|
embedding = self._embedding_model.encode([text])[0]
|
|
23
26
|
return embedding.tolist()
|
|
24
27
|
except ImportError:
|
|
28
|
+
logger.debug("sentence-transformers not installed; skipping term embedding")
|
|
29
|
+
return None
|
|
30
|
+
except Exception as exc:
|
|
31
|
+
logger.debug("Term embedding unavailable: %s", exc)
|
|
25
32
|
return None
|
|
26
33
|
|
|
27
34
|
def extract_terms(self, text: str) -> Set[str]:
|