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.
Files changed (41) hide show
  1. memtru-0.1.0/.gitignore +18 -0
  2. memtru-0.1.0/LICENSE +53 -0
  3. memtru-0.1.0/PKG-INFO +143 -0
  4. memtru-0.1.0/README.md +50 -0
  5. memtru-0.1.0/pyproject.toml +94 -0
  6. memtru-0.1.0/src/memtru/__init__.py +7 -0
  7. memtru-0.1.0/src/memtru/adapters/__init__.py +105 -0
  8. memtru-0.1.0/src/memtru/adapters/base.py +80 -0
  9. memtru-0.1.0/src/memtru/adapters/http.py +239 -0
  10. memtru-0.1.0/src/memtru/adapters/in_memory.py +444 -0
  11. memtru-0.1.0/src/memtru/adapters/lifecycle.py +122 -0
  12. memtru-0.1.0/src/memtru/adapters/mem0.py +331 -0
  13. memtru-0.1.0/src/memtru/cli/__init__.py +0 -0
  14. memtru-0.1.0/src/memtru/cli/app.py +574 -0
  15. memtru-0.1.0/src/memtru/cloud/__init__.py +146 -0
  16. memtru-0.1.0/src/memtru/core/__init__.py +0 -0
  17. memtru-0.1.0/src/memtru/core/config.py +221 -0
  18. memtru-0.1.0/src/memtru/core/containment.py +172 -0
  19. memtru-0.1.0/src/memtru/core/engine.py +414 -0
  20. memtru-0.1.0/src/memtru/core/ledger.py +36 -0
  21. memtru-0.1.0/src/memtru/core/models.py +120 -0
  22. memtru-0.1.0/src/memtru/errors.py +65 -0
  23. memtru-0.1.0/src/memtru/reporting/__init__.py +45 -0
  24. memtru-0.1.0/src/memtru/reporting/compare.py +133 -0
  25. memtru-0.1.0/src/memtru/reporting/export_schema.py +31 -0
  26. memtru-0.1.0/src/memtru/reporting/html.py +41 -0
  27. memtru-0.1.0/src/memtru/reporting/schema.py +199 -0
  28. memtru-0.1.0/src/memtru/reporting/templates/report.html.j2 +231 -0
  29. memtru-0.1.0/src/memtru/reporting/terminal.py +165 -0
  30. memtru-0.1.0/src/memtru/scenarios/__init__.py +62 -0
  31. memtru-0.1.0/src/memtru/scenarios/afterlife.py +360 -0
  32. memtru-0.1.0/src/memtru/scenarios/base.py +390 -0
  33. memtru-0.1.0/src/memtru/scenarios/isolation.py +222 -0
  34. memtru-0.1.0/src/memtru/scenarios/lifecycle.py +332 -0
  35. memtru-0.1.0/src/memtru/scenarios/poisoning.py +89 -0
  36. memtru-0.1.0/src/memtru/security/__init__.py +0 -0
  37. memtru-0.1.0/src/memtru/security/redaction.py +87 -0
  38. memtru-0.1.0/src/memtru/security/sanitize.py +56 -0
  39. memtru-0.1.0/src/memtru/security/urls.py +101 -0
  40. memtru-0.1.0/src/memtru/testing/__init__.py +1 -0
  41. memtru-0.1.0/src/memtru/testing/reference_server.py +195 -0
@@ -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,7 @@
1
+ """Memtru: reproducible reliability and isolation tests for AI agent memory."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ REPORT_SCHEMA_VERSION = "1.0"
6
+
7
+ __all__ = ["REPORT_SCHEMA_VERSION", "__version__"]
@@ -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: ...