memtru 0.1.0__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.
- memtru-0.1.0/.gitignore +18 -0
- memtru-0.1.0/LICENSE +53 -0
- memtru-0.1.0/PKG-INFO +143 -0
- memtru-0.1.0/README.md +50 -0
- memtru-0.1.0/pyproject.toml +94 -0
- memtru-0.1.0/src/memtru/__init__.py +7 -0
- memtru-0.1.0/src/memtru/adapters/__init__.py +105 -0
- memtru-0.1.0/src/memtru/adapters/base.py +80 -0
- memtru-0.1.0/src/memtru/adapters/http.py +239 -0
- memtru-0.1.0/src/memtru/adapters/in_memory.py +444 -0
- memtru-0.1.0/src/memtru/adapters/lifecycle.py +122 -0
- memtru-0.1.0/src/memtru/adapters/mem0.py +331 -0
- memtru-0.1.0/src/memtru/cli/__init__.py +0 -0
- memtru-0.1.0/src/memtru/cli/app.py +574 -0
- memtru-0.1.0/src/memtru/cloud/__init__.py +146 -0
- memtru-0.1.0/src/memtru/core/__init__.py +0 -0
- memtru-0.1.0/src/memtru/core/config.py +221 -0
- memtru-0.1.0/src/memtru/core/containment.py +172 -0
- memtru-0.1.0/src/memtru/core/engine.py +414 -0
- memtru-0.1.0/src/memtru/core/ledger.py +36 -0
- memtru-0.1.0/src/memtru/core/models.py +120 -0
- memtru-0.1.0/src/memtru/errors.py +65 -0
- memtru-0.1.0/src/memtru/reporting/__init__.py +45 -0
- memtru-0.1.0/src/memtru/reporting/compare.py +133 -0
- memtru-0.1.0/src/memtru/reporting/export_schema.py +31 -0
- memtru-0.1.0/src/memtru/reporting/html.py +41 -0
- memtru-0.1.0/src/memtru/reporting/schema.py +199 -0
- memtru-0.1.0/src/memtru/reporting/templates/report.html.j2 +231 -0
- memtru-0.1.0/src/memtru/reporting/terminal.py +165 -0
- memtru-0.1.0/src/memtru/scenarios/__init__.py +62 -0
- memtru-0.1.0/src/memtru/scenarios/afterlife.py +360 -0
- memtru-0.1.0/src/memtru/scenarios/base.py +390 -0
- memtru-0.1.0/src/memtru/scenarios/isolation.py +222 -0
- memtru-0.1.0/src/memtru/scenarios/lifecycle.py +332 -0
- memtru-0.1.0/src/memtru/scenarios/poisoning.py +89 -0
- memtru-0.1.0/src/memtru/security/__init__.py +0 -0
- memtru-0.1.0/src/memtru/security/redaction.py +87 -0
- memtru-0.1.0/src/memtru/security/sanitize.py +56 -0
- memtru-0.1.0/src/memtru/security/urls.py +101 -0
- memtru-0.1.0/src/memtru/testing/__init__.py +1 -0
- memtru-0.1.0/src/memtru/testing/reference_server.py +195 -0
memtru-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.egg-info/
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.mypy_cache/
|
|
6
|
+
.ruff_cache/
|
|
7
|
+
.coverage
|
|
8
|
+
.memtru/
|
|
9
|
+
.scratch/
|
|
10
|
+
node_modules/
|
|
11
|
+
.next/
|
|
12
|
+
apps/web/data/
|
|
13
|
+
apps/web/test-results/
|
|
14
|
+
apps/web/playwright-report/
|
|
15
|
+
*.tsbuildinfo
|
|
16
|
+
.env
|
|
17
|
+
.env*.local
|
|
18
|
+
.memoryproof/
|
memtru-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Memtru CLI License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 the Memtru copyright holder. All rights reserved.
|
|
4
|
+
|
|
5
|
+
The Memtru command-line tool and Python package (the "Software") are
|
|
6
|
+
proprietary. They are not open source.
|
|
7
|
+
|
|
8
|
+
1. Permitted use. Subject to these terms, you may install and run the Software,
|
|
9
|
+
free of charge, to test memory systems that you own or are authorized to
|
|
10
|
+
test, including in your continuous-integration pipelines, and you may use the
|
|
11
|
+
reports it produces for any purpose.
|
|
12
|
+
|
|
13
|
+
2. Restrictions. You may not, and may not allow others to:
|
|
14
|
+
a. copy, modify, translate or create derivative works of the Software,
|
|
15
|
+
except copies needed to install and run it as permitted above;
|
|
16
|
+
b. distribute, sublicense, sell, rent, lend or otherwise make the Software
|
|
17
|
+
available to third parties, including as part of another product or a
|
|
18
|
+
hosted service;
|
|
19
|
+
c. reverse engineer, decompile or disassemble the Software, except to the
|
|
20
|
+
extent applicable law expressly permits it despite this restriction;
|
|
21
|
+
d. use the Software, or knowledge gained from its code, to build a competing
|
|
22
|
+
product or service;
|
|
23
|
+
e. remove or alter any notice of ownership in the Software.
|
|
24
|
+
Writing your own adapter that loads into the Software through its documented
|
|
25
|
+
interfaces is not a derivative work of the Software.
|
|
26
|
+
|
|
27
|
+
3. Testing responsibility. Run the Software only against systems you are
|
|
28
|
+
authorized to test. You are responsible for its effects on those systems.
|
|
29
|
+
|
|
30
|
+
4. Memtru services. Use of the hosted Memtru service (for example uploading
|
|
31
|
+
reports to memtru.com) is governed by its terms at https://memtru.com/terms.
|
|
32
|
+
|
|
33
|
+
5. Ownership and feedback. The copyright holder keeps all rights not expressly
|
|
34
|
+
granted here. Feedback you provide may be used without obligation to you.
|
|
35
|
+
|
|
36
|
+
6. Termination. This license ends automatically if you breach it. On
|
|
37
|
+
termination you must stop using the Software and delete all copies.
|
|
38
|
+
|
|
39
|
+
7. No warranty. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
40
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
41
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. TEST
|
|
42
|
+
RESULTS DESCRIBE THE SCENARIOS THAT RAN AND ARE NOT A GUARANTEE OF SECURITY.
|
|
43
|
+
|
|
44
|
+
8. Limitation of liability. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT
|
|
45
|
+
SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
46
|
+
LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR ITS USE.
|
|
47
|
+
|
|
48
|
+
9. Other agreements. A separate written agreement signed by the copyright
|
|
49
|
+
holder takes precedence over these terms.
|
|
50
|
+
|
|
51
|
+
Third-party components used by the Software remain under their own licenses.
|
|
52
|
+
|
|
53
|
+
Questions: contact@memtru.com
|
memtru-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: memtru
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reproducible reliability and isolation tests for AI agent memory systems.
|
|
5
|
+
Project-URL: Homepage, https://memtru.com
|
|
6
|
+
Project-URL: Documentation, https://memtru.com/docs
|
|
7
|
+
Project-URL: Demo, https://memtru.com/demo
|
|
8
|
+
Author-email: Memtru <contact@memtru.com>
|
|
9
|
+
License: Memtru CLI License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 the Memtru copyright holder. All rights reserved.
|
|
12
|
+
|
|
13
|
+
The Memtru command-line tool and Python package (the "Software") are
|
|
14
|
+
proprietary. They are not open source.
|
|
15
|
+
|
|
16
|
+
1. Permitted use. Subject to these terms, you may install and run the Software,
|
|
17
|
+
free of charge, to test memory systems that you own or are authorized to
|
|
18
|
+
test, including in your continuous-integration pipelines, and you may use the
|
|
19
|
+
reports it produces for any purpose.
|
|
20
|
+
|
|
21
|
+
2. Restrictions. You may not, and may not allow others to:
|
|
22
|
+
a. copy, modify, translate or create derivative works of the Software,
|
|
23
|
+
except copies needed to install and run it as permitted above;
|
|
24
|
+
b. distribute, sublicense, sell, rent, lend or otherwise make the Software
|
|
25
|
+
available to third parties, including as part of another product or a
|
|
26
|
+
hosted service;
|
|
27
|
+
c. reverse engineer, decompile or disassemble the Software, except to the
|
|
28
|
+
extent applicable law expressly permits it despite this restriction;
|
|
29
|
+
d. use the Software, or knowledge gained from its code, to build a competing
|
|
30
|
+
product or service;
|
|
31
|
+
e. remove or alter any notice of ownership in the Software.
|
|
32
|
+
Writing your own adapter that loads into the Software through its documented
|
|
33
|
+
interfaces is not a derivative work of the Software.
|
|
34
|
+
|
|
35
|
+
3. Testing responsibility. Run the Software only against systems you are
|
|
36
|
+
authorized to test. You are responsible for its effects on those systems.
|
|
37
|
+
|
|
38
|
+
4. Memtru services. Use of the hosted Memtru service (for example uploading
|
|
39
|
+
reports to memtru.com) is governed by its terms at https://memtru.com/terms.
|
|
40
|
+
|
|
41
|
+
5. Ownership and feedback. The copyright holder keeps all rights not expressly
|
|
42
|
+
granted here. Feedback you provide may be used without obligation to you.
|
|
43
|
+
|
|
44
|
+
6. Termination. This license ends automatically if you breach it. On
|
|
45
|
+
termination you must stop using the Software and delete all copies.
|
|
46
|
+
|
|
47
|
+
7. No warranty. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
48
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
49
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. TEST
|
|
50
|
+
RESULTS DESCRIBE THE SCENARIOS THAT RAN AND ARE NOT A GUARANTEE OF SECURITY.
|
|
51
|
+
|
|
52
|
+
8. Limitation of liability. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT
|
|
53
|
+
SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
54
|
+
LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR ITS USE.
|
|
55
|
+
|
|
56
|
+
9. Other agreements. A separate written agreement signed by the copyright
|
|
57
|
+
holder takes precedence over these terms.
|
|
58
|
+
|
|
59
|
+
Third-party components used by the Software remain under their own licenses.
|
|
60
|
+
|
|
61
|
+
Questions: contact@memtru.com
|
|
62
|
+
License-File: LICENSE
|
|
63
|
+
Keywords: ai agents,deletion,langgraph,llm,mem0,memory,privacy,testing
|
|
64
|
+
Classifier: Development Status :: 4 - Beta
|
|
65
|
+
Classifier: Environment :: Console
|
|
66
|
+
Classifier: Intended Audience :: Developers
|
|
67
|
+
Classifier: License :: Other/Proprietary License
|
|
68
|
+
Classifier: Operating System :: OS Independent
|
|
69
|
+
Classifier: Programming Language :: Python :: 3
|
|
70
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
71
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
72
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
73
|
+
Classifier: Topic :: Security
|
|
74
|
+
Classifier: Topic :: Software Development :: Testing
|
|
75
|
+
Requires-Python: >=3.11
|
|
76
|
+
Requires-Dist: httpx<1,>=0.27
|
|
77
|
+
Requires-Dist: jinja2>=3.1
|
|
78
|
+
Requires-Dist: pydantic<3,>=2.9
|
|
79
|
+
Requires-Dist: pyyaml>=6.0
|
|
80
|
+
Requires-Dist: rich>=13.7
|
|
81
|
+
Requires-Dist: typer<1,>=0.12
|
|
82
|
+
Provides-Extra: dev
|
|
83
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
84
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
85
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
86
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
87
|
+
Requires-Dist: types-pyyaml>=6; extra == 'dev'
|
|
88
|
+
Provides-Extra: langgraph
|
|
89
|
+
Requires-Dist: langgraph==1.2.16; extra == 'langgraph'
|
|
90
|
+
Provides-Extra: mem0
|
|
91
|
+
Requires-Dist: mem0ai==2.2.1; extra == 'mem0'
|
|
92
|
+
Description-Content-Type: text/markdown
|
|
93
|
+
|
|
94
|
+
# Memtru
|
|
95
|
+
|
|
96
|
+
Reproducible tests for the memory systems behind AI agents. Memtru writes synthetic markers into your memory store and checks the boundaries that matter: one user's memory never reaches another user or tenant, deleted memory stays deleted, updates replace stale facts, and sessions stay separate.
|
|
97
|
+
|
|
98
|
+
Every finding comes with the evidence to reproduce it. Tests run locally or in CI; nothing is uploaded unless you run `memtru upload`.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install memtru
|
|
102
|
+
memtru demo # a deliberately broken store, then a fixed one
|
|
103
|
+
memtru demo --afterlife # a deleted memory that comes back after a later event
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Test your own system
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
memtru init # writes memtru.yaml
|
|
110
|
+
memtru doctor # checks the configuration and adapter
|
|
111
|
+
memtru test # runs the scenarios, writes JSON and HTML reports
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Adapters: the built-in reference store (`in_memory`), any service implementing the Memtru REST contract (`http`), your own Python class (`custom`), and Mem0 OSS (`pip install "memtru[mem0]"`).
|
|
115
|
+
|
|
116
|
+
## Scenarios
|
|
117
|
+
|
|
118
|
+
| Id | What it checks |
|
|
119
|
+
|---|---|
|
|
120
|
+
| MT-ISO-001 | Cross-user isolation |
|
|
121
|
+
| MT-TEN-002 | Cross-tenant isolation |
|
|
122
|
+
| MT-DEL-003 | Deleted memory stops being retrievable |
|
|
123
|
+
| MT-STALE-004 | Updated facts replace stale ones |
|
|
124
|
+
| MT-SES-005 | Session isolation |
|
|
125
|
+
| MT-INT-006 | Updates change only the intended record |
|
|
126
|
+
| MT-POISON-007 | Controlled memory poisoning (agent mode) |
|
|
127
|
+
| MT-AF-001 | Afterlife: a late write brings deleted memory back |
|
|
128
|
+
| MT-AF-002 | Afterlife: a new session rebuilds deleted memory from history |
|
|
129
|
+
|
|
130
|
+
Every scenario reports PASS, FAIL, INCONCLUSIVE, ERROR or SKIPPED. A result is never PASS without a positive control showing the authorized path works.
|
|
131
|
+
|
|
132
|
+
Exit codes make it CI-ready: `0` pass, `1` a scenario failed, `3` execution error, `4` inconclusive when `--fail-on-inconclusive` is set.
|
|
133
|
+
|
|
134
|
+
## Links
|
|
135
|
+
|
|
136
|
+
- Documentation: https://memtru.com/docs
|
|
137
|
+
- Interactive demo: https://memtru.com/demo
|
|
138
|
+
- Dashboard for uploaded runs: https://memtru.com
|
|
139
|
+
- Security reports: security@memtru.com · Contact: contact@memtru.com
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
Proprietary. Free to install and use for testing systems you own or are authorized to test; see the LICENSE file for the terms. Test results describe the scenarios that ran and are not a guarantee of security.
|
memtru-0.1.0/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Memtru
|
|
2
|
+
|
|
3
|
+
Reproducible tests for the memory systems behind AI agents. Memtru writes synthetic markers into your memory store and checks the boundaries that matter: one user's memory never reaches another user or tenant, deleted memory stays deleted, updates replace stale facts, and sessions stay separate.
|
|
4
|
+
|
|
5
|
+
Every finding comes with the evidence to reproduce it. Tests run locally or in CI; nothing is uploaded unless you run `memtru upload`.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install memtru
|
|
9
|
+
memtru demo # a deliberately broken store, then a fixed one
|
|
10
|
+
memtru demo --afterlife # a deleted memory that comes back after a later event
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Test your own system
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
memtru init # writes memtru.yaml
|
|
17
|
+
memtru doctor # checks the configuration and adapter
|
|
18
|
+
memtru test # runs the scenarios, writes JSON and HTML reports
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Adapters: the built-in reference store (`in_memory`), any service implementing the Memtru REST contract (`http`), your own Python class (`custom`), and Mem0 OSS (`pip install "memtru[mem0]"`).
|
|
22
|
+
|
|
23
|
+
## Scenarios
|
|
24
|
+
|
|
25
|
+
| Id | What it checks |
|
|
26
|
+
|---|---|
|
|
27
|
+
| MT-ISO-001 | Cross-user isolation |
|
|
28
|
+
| MT-TEN-002 | Cross-tenant isolation |
|
|
29
|
+
| MT-DEL-003 | Deleted memory stops being retrievable |
|
|
30
|
+
| MT-STALE-004 | Updated facts replace stale ones |
|
|
31
|
+
| MT-SES-005 | Session isolation |
|
|
32
|
+
| MT-INT-006 | Updates change only the intended record |
|
|
33
|
+
| MT-POISON-007 | Controlled memory poisoning (agent mode) |
|
|
34
|
+
| MT-AF-001 | Afterlife: a late write brings deleted memory back |
|
|
35
|
+
| MT-AF-002 | Afterlife: a new session rebuilds deleted memory from history |
|
|
36
|
+
|
|
37
|
+
Every scenario reports PASS, FAIL, INCONCLUSIVE, ERROR or SKIPPED. A result is never PASS without a positive control showing the authorized path works.
|
|
38
|
+
|
|
39
|
+
Exit codes make it CI-ready: `0` pass, `1` a scenario failed, `3` execution error, `4` inconclusive when `--fail-on-inconclusive` is set.
|
|
40
|
+
|
|
41
|
+
## Links
|
|
42
|
+
|
|
43
|
+
- Documentation: https://memtru.com/docs
|
|
44
|
+
- Interactive demo: https://memtru.com/demo
|
|
45
|
+
- Dashboard for uploaded runs: https://memtru.com
|
|
46
|
+
- Security reports: security@memtru.com · Contact: contact@memtru.com
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
Proprietary. Free to install and use for testing systems you own or are authorized to test; see the LICENSE file for the terms. Test results describe the scenarios that ran and are not a guarantee of security.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "memtru"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Reproducible reliability and isolation tests for AI agent memory systems."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [{ name = "Memtru", email = "contact@memtru.com" }]
|
|
13
|
+
keywords = ["ai agents", "memory", "testing", "privacy", "deletion", "llm", "mem0", "langgraph"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: Other/Proprietary License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Testing",
|
|
25
|
+
"Topic :: Security",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"typer>=0.12,<1",
|
|
29
|
+
"rich>=13.7",
|
|
30
|
+
"pydantic>=2.9,<3",
|
|
31
|
+
"pyyaml>=6.0",
|
|
32
|
+
"jinja2>=3.1",
|
|
33
|
+
"httpx>=0.27,<1",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
mem0 = ["mem0ai==2.2.1"]
|
|
38
|
+
# Afterlife integration example (examples/afterlife-langgraph); pinned on purpose.
|
|
39
|
+
langgraph = ["langgraph==1.2.16"]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=8.3",
|
|
42
|
+
"pytest-cov>=5",
|
|
43
|
+
"ruff>=0.6",
|
|
44
|
+
"mypy>=1.11",
|
|
45
|
+
"types-PyYAML>=6",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://memtru.com"
|
|
50
|
+
Documentation = "https://memtru.com/docs"
|
|
51
|
+
Demo = "https://memtru.com/demo"
|
|
52
|
+
|
|
53
|
+
[project.scripts]
|
|
54
|
+
memtru = "memtru.cli.app:main"
|
|
55
|
+
|
|
56
|
+
[tool.hatch.build.targets.wheel]
|
|
57
|
+
packages = ["src/memtru"]
|
|
58
|
+
|
|
59
|
+
# The source distribution ships the package only: no tests, fixtures or examples.
|
|
60
|
+
[tool.hatch.build.targets.sdist]
|
|
61
|
+
include = ["src/memtru", "README.md", "LICENSE", "pyproject.toml"]
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
testpaths = ["tests"]
|
|
65
|
+
addopts = "-q"
|
|
66
|
+
pythonpath = ["."]
|
|
67
|
+
|
|
68
|
+
[tool.ruff]
|
|
69
|
+
line-length = 100
|
|
70
|
+
target-version = "py311"
|
|
71
|
+
src = ["src", "tests"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint]
|
|
74
|
+
select = ["E", "F", "W", "I", "B", "UP", "S", "RUF", "SIM", "PT"]
|
|
75
|
+
ignore = [
|
|
76
|
+
"S101", # assert is the idiom in pytest and for type narrowing
|
|
77
|
+
"E501", # line length is enforced by `ruff format`; long strings are allowed
|
|
78
|
+
"RUF001", # status glyphs (en dash, check marks) are intentional
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[tool.ruff.lint.per-file-ignores]
|
|
82
|
+
"tests/**" = ["S105", "S106", "S108", "S310", "PT018"]
|
|
83
|
+
"src/memtru/cli/app.py" = ["PT028"] # `test` is a CLI command, not a pytest test
|
|
84
|
+
|
|
85
|
+
[tool.mypy]
|
|
86
|
+
python_version = "3.11"
|
|
87
|
+
strict = true
|
|
88
|
+
packages = ["memtru"]
|
|
89
|
+
mypy_path = "src"
|
|
90
|
+
plugins = ["pydantic.mypy"]
|
|
91
|
+
|
|
92
|
+
[[tool.mypy.overrides]]
|
|
93
|
+
module = "tests.*"
|
|
94
|
+
disallow_untyped_defs = false
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Adapter registry: builds the adapter described by the configuration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
from memtru.adapters.base import AgentAdapter, MemoryAdapter
|
|
11
|
+
from memtru.adapters.in_memory import InMemoryAdapter
|
|
12
|
+
from memtru.core.config import (
|
|
13
|
+
Config,
|
|
14
|
+
CustomAdapterConfig,
|
|
15
|
+
HttpAdapterConfig,
|
|
16
|
+
InMemoryAdapterConfig,
|
|
17
|
+
Mem0AdapterConfig,
|
|
18
|
+
)
|
|
19
|
+
from memtru.errors import ConfigError
|
|
20
|
+
|
|
21
|
+
__all__ = ["AgentAdapter", "MemoryAdapter", "build_adapter", "describe_builtin_adapters"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class AdapterInfo:
|
|
26
|
+
type: str
|
|
27
|
+
summary: str
|
|
28
|
+
verification: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
BUILTIN_ADAPTERS = [
|
|
32
|
+
AdapterInfo(
|
|
33
|
+
"in_memory",
|
|
34
|
+
"Built-in reference store with tenant, user and session scoping. Supports fault injection.",
|
|
35
|
+
"Verified by the Memtru test suite.",
|
|
36
|
+
),
|
|
37
|
+
AdapterInfo(
|
|
38
|
+
"http",
|
|
39
|
+
"Any service implementing the Memtru REST contract (docs/adapters.md).",
|
|
40
|
+
"Verified against the reference server in examples/provider-integration.",
|
|
41
|
+
),
|
|
42
|
+
AdapterInfo(
|
|
43
|
+
"mem0",
|
|
44
|
+
"Mem0 OSS 2.2.1; explicit SDK factory, synthetic search-filter testing.",
|
|
45
|
+
"SDK contract tests and local Qdrant integration; hosted verification is separate.",
|
|
46
|
+
),
|
|
47
|
+
AdapterInfo(
|
|
48
|
+
"custom",
|
|
49
|
+
"Your own Python class implementing memtru.adapters.base.MemoryAdapter.",
|
|
50
|
+
"Verified by you: Memtru only loads it.",
|
|
51
|
+
),
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def describe_builtin_adapters() -> list[AdapterInfo]:
|
|
56
|
+
return list(BUILTIN_ADAPTERS)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _load_custom(config: CustomAdapterConfig) -> MemoryAdapter:
|
|
60
|
+
module_name, _, attr = config.entrypoint.partition(":")
|
|
61
|
+
# Like pytest, resolve user modules relative to the directory the CLI runs in.
|
|
62
|
+
if os.getcwd() not in sys.path:
|
|
63
|
+
sys.path.insert(0, os.getcwd())
|
|
64
|
+
try:
|
|
65
|
+
module = importlib.import_module(module_name)
|
|
66
|
+
except ImportError as err:
|
|
67
|
+
raise ConfigError(
|
|
68
|
+
f"Cannot import adapter module {module_name!r}: {err}",
|
|
69
|
+
hint="Make sure the module is importable from the current directory or PYTHONPATH.",
|
|
70
|
+
) from None
|
|
71
|
+
factory = getattr(module, attr, None)
|
|
72
|
+
if factory is None:
|
|
73
|
+
raise ConfigError(f"{module_name!r} has no attribute {attr!r}.")
|
|
74
|
+
adapter = factory(**config.options)
|
|
75
|
+
if not isinstance(adapter, MemoryAdapter):
|
|
76
|
+
raise ConfigError(f"{config.entrypoint} did not produce a MemoryAdapter instance.")
|
|
77
|
+
return adapter
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_adapter(config: Config) -> MemoryAdapter:
|
|
81
|
+
adapter_config = config.adapter
|
|
82
|
+
if isinstance(adapter_config, InMemoryAdapterConfig):
|
|
83
|
+
return InMemoryAdapter(adapter_config.faults, adapter_config.index_delay_ms)
|
|
84
|
+
if isinstance(adapter_config, HttpAdapterConfig):
|
|
85
|
+
from memtru.adapters.http import HttpAdapter
|
|
86
|
+
|
|
87
|
+
return HttpAdapter(adapter_config, environment=config.execution.environment)
|
|
88
|
+
if isinstance(adapter_config, CustomAdapterConfig):
|
|
89
|
+
return _load_custom(adapter_config)
|
|
90
|
+
if isinstance(adapter_config, Mem0AdapterConfig):
|
|
91
|
+
from memtru.adapters.mem0 import Mem0Adapter
|
|
92
|
+
|
|
93
|
+
# The optional SDK otherwise enables its own telemetry at import time.
|
|
94
|
+
os.environ["MEM0_TELEMETRY"] = "false"
|
|
95
|
+
module_name, _, attr = adapter_config.factory.partition(":")
|
|
96
|
+
if os.getcwd() not in sys.path:
|
|
97
|
+
sys.path.insert(0, os.getcwd())
|
|
98
|
+
try:
|
|
99
|
+
factory = getattr(importlib.import_module(module_name), attr)
|
|
100
|
+
except (ImportError, AttributeError):
|
|
101
|
+
raise ConfigError(
|
|
102
|
+
"Cannot load Mem0 factory. Check adapter.factory and install memtru[mem0]."
|
|
103
|
+
) from None
|
|
104
|
+
return Mem0Adapter(factory())
|
|
105
|
+
raise ConfigError(f"Unknown adapter type {adapter_config!r}") # pragma: no cover
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""The adapter contract every memory provider integration implements.
|
|
2
|
+
|
|
3
|
+
Adapters translate Memtru operations into calls on a real memory system.
|
|
4
|
+
They must scope every operation to the given :class:`MemoryContext`; the
|
|
5
|
+
scenarios then verify whether that scoping actually holds.
|
|
6
|
+
|
|
7
|
+
Optional operations raise :class:`UnsupportedCapabilityError` by default.
|
|
8
|
+
Declare what is supported in :meth:`MemoryAdapter.capabilities` so scenarios
|
|
9
|
+
can be skipped, never silently passed, when a prerequisite is missing.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from typing import ClassVar
|
|
16
|
+
|
|
17
|
+
from memtru.core.models import Capability, MemoryContext, MemoryRecord, MemoryScope
|
|
18
|
+
from memtru.errors import UnsupportedCapabilityError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MemoryAdapter(ABC):
|
|
22
|
+
name: ClassVar[str]
|
|
23
|
+
version: ClassVar[str] = "1.0"
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def capabilities(self) -> frozenset[Capability]:
|
|
27
|
+
"""Capabilities this adapter supports *and* boundaries it claims to enforce."""
|
|
28
|
+
|
|
29
|
+
def setup(self, namespace: str) -> None: # noqa: B027 - optional hook
|
|
30
|
+
"""Prepare an isolated test namespace. Called once per run."""
|
|
31
|
+
|
|
32
|
+
def teardown(self, namespace: str) -> None: # noqa: B027 - optional hook
|
|
33
|
+
"""Release the namespace created by :meth:`setup`. Must not touch other data."""
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def add(
|
|
37
|
+
self,
|
|
38
|
+
ctx: MemoryContext,
|
|
39
|
+
content: str,
|
|
40
|
+
*,
|
|
41
|
+
metadata: dict[str, str] | None = None,
|
|
42
|
+
scope: MemoryScope = "user",
|
|
43
|
+
) -> MemoryRecord: ...
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def search(self, ctx: MemoryContext, query: str, *, limit: int = 10) -> list[MemoryRecord]: ...
|
|
47
|
+
|
|
48
|
+
def get(self, ctx: MemoryContext, memory_id: str) -> MemoryRecord | None:
|
|
49
|
+
raise UnsupportedCapabilityError(f"{self.name} does not support read by id")
|
|
50
|
+
|
|
51
|
+
def update(
|
|
52
|
+
self,
|
|
53
|
+
ctx: MemoryContext,
|
|
54
|
+
memory_id: str,
|
|
55
|
+
content: str,
|
|
56
|
+
*,
|
|
57
|
+
metadata: dict[str, str] | None = None,
|
|
58
|
+
) -> MemoryRecord:
|
|
59
|
+
raise UnsupportedCapabilityError(f"{self.name} does not support updates")
|
|
60
|
+
|
|
61
|
+
def delete(self, ctx: MemoryContext, memory_id: str) -> None:
|
|
62
|
+
raise UnsupportedCapabilityError(f"{self.name} does not support deletion")
|
|
63
|
+
|
|
64
|
+
def describe(self) -> dict[str, str]:
|
|
65
|
+
"""Safe, non-secret metadata included in reports."""
|
|
66
|
+
return {}
|
|
67
|
+
|
|
68
|
+
def close(self) -> None: # noqa: B027 - optional hook
|
|
69
|
+
"""Release connections."""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class AgentAdapter(MemoryAdapter):
|
|
73
|
+
"""An adapter that can also run the agent on top of the memory store.
|
|
74
|
+
|
|
75
|
+
Required for agent-mode scenarios (MT-POISON-007). ``respond`` must run the
|
|
76
|
+
real agent with the memory visible to ``ctx`` and return its final output.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def respond(self, ctx: MemoryContext, prompt: str) -> str: ...
|