grounded-memory 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.
- grounded_memory-0.1.0/LICENSE +21 -0
- grounded_memory-0.1.0/PKG-INFO +305 -0
- grounded_memory-0.1.0/README.md +257 -0
- grounded_memory-0.1.0/pyproject.toml +106 -0
- grounded_memory-0.1.0/setup.cfg +4 -0
- grounded_memory-0.1.0/src/gmem/__init__.py +35 -0
- grounded_memory-0.1.0/src/gmem/service.py +5 -0
- grounded_memory-0.1.0/src/grounded_memory/__init__.py +114 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/__init__.py +46 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/base_agent.py +55 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/discovery.py +251 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/generic_agent.py +783 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/__init__.py +95 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/agent.py +121 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/constraints.py +471 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/extractor.py +424 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/kb_manager.py +207 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/knowledge.py +403 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/lifecycle.py +138 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/loaders/__init__.py +16 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/loaders/cache.py +219 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/loaders/openfda.py +172 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/loaders/rxnorm.py +206 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/models.py +129 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/healthcare/retrieval.py +788 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/identity_service.py +103 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/registry.py +215 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/result.py +20 -0
- grounded_memory-0.1.0/src/grounded_memory/adapters/seeds.py +322 -0
- grounded_memory-0.1.0/src/grounded_memory/configs/engineering_constraints.yaml +187 -0
- grounded_memory-0.1.0/src/grounded_memory/configs/finance_constraints.yaml +175 -0
- grounded_memory-0.1.0/src/grounded_memory/configs/generic_constraints.yaml +174 -0
- grounded_memory-0.1.0/src/grounded_memory/configs/healthcare_constraints.yaml +282 -0
- grounded_memory-0.1.0/src/grounded_memory/configs/healthcare_kb.yaml +126 -0
- grounded_memory-0.1.0/src/grounded_memory/configs/legal_constraints.yaml +182 -0
- grounded_memory-0.1.0/src/grounded_memory/configs/llm_config.yaml +50 -0
- grounded_memory-0.1.0/src/grounded_memory/configs/neo4j_config.yaml +44 -0
- grounded_memory-0.1.0/src/grounded_memory/core/__init__.py +122 -0
- grounded_memory-0.1.0/src/grounded_memory/core/conflict_resolution.py +323 -0
- grounded_memory-0.1.0/src/grounded_memory/core/constraints.py +1022 -0
- grounded_memory-0.1.0/src/grounded_memory/core/entity_identity.py +62 -0
- grounded_memory-0.1.0/src/grounded_memory/core/grounding.py +722 -0
- grounded_memory-0.1.0/src/grounded_memory/core/hybrid_store.py +342 -0
- grounded_memory-0.1.0/src/grounded_memory/core/intent.py +342 -0
- grounded_memory-0.1.0/src/grounded_memory/core/models.py +474 -0
- grounded_memory-0.1.0/src/grounded_memory/core/neo4j_store.py +1124 -0
- grounded_memory-0.1.0/src/grounded_memory/core/postgres_hybrid_store.py +264 -0
- grounded_memory-0.1.0/src/grounded_memory/core/postgres_store.py +1505 -0
- grounded_memory-0.1.0/src/grounded_memory/core/store.py +548 -0
- grounded_memory-0.1.0/src/grounded_memory/core/system.py +133 -0
- grounded_memory-0.1.0/src/grounded_memory/core/tuple_normalization.py +176 -0
- grounded_memory-0.1.0/src/grounded_memory/llm/__init__.py +52 -0
- grounded_memory-0.1.0/src/grounded_memory/llm/client.py +558 -0
- grounded_memory-0.1.0/src/grounded_memory/llm/extractor.py +72 -0
- grounded_memory-0.1.0/src/grounded_memory/llm/prompts.py +437 -0
- grounded_memory-0.1.0/src/grounded_memory/logging_utils.py +60 -0
- grounded_memory-0.1.0/src/grounded_memory/memory.py +2267 -0
- grounded_memory-0.1.0/src/grounded_memory/retrieval/__init__.py +17 -0
- grounded_memory-0.1.0/src/grounded_memory/retrieval/graph.py +1474 -0
- grounded_memory-0.1.0/src/grounded_memory/service/__init__.py +5 -0
- grounded_memory-0.1.0/src/grounded_memory/service/app.py +331 -0
- grounded_memory-0.1.0/src/grounded_memory/service/models.py +130 -0
- grounded_memory-0.1.0/src/grounded_memory/system.py +106 -0
- grounded_memory-0.1.0/src/grounded_memory.egg-info/PKG-INFO +305 -0
- grounded_memory-0.1.0/src/grounded_memory.egg-info/SOURCES.txt +74 -0
- grounded_memory-0.1.0/src/grounded_memory.egg-info/dependency_links.txt +1 -0
- grounded_memory-0.1.0/src/grounded_memory.egg-info/requires.txt +34 -0
- grounded_memory-0.1.0/src/grounded_memory.egg-info/top_level.txt +2 -0
- grounded_memory-0.1.0/tests/test_adapters.py +290 -0
- grounded_memory-0.1.0/tests/test_end_to_end.py +443 -0
- grounded_memory-0.1.0/tests/test_governance.py +574 -0
- grounded_memory-0.1.0/tests/test_healthcare_reconciliation.py +372 -0
- grounded_memory-0.1.0/tests/test_healthcare_retrieval.py +676 -0
- grounded_memory-0.1.0/tests/test_hybrid_storage.py +563 -0
- grounded_memory-0.1.0/tests/test_memory_layer.py +58 -0
- grounded_memory-0.1.0/tests/test_postgres_storage.py +122 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Faycal & Marouane
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: grounded-memory
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Correctness-First Structured Memory Layer for LLM Agents
|
|
5
|
+
Author: Faycal & Marouane
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/marouaneoa/GroundedMemory
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: pydantic>=2.0
|
|
21
|
+
Requires-Dist: typing-extensions>=4.14.1
|
|
22
|
+
Requires-Dist: python-dateutil>=2.8
|
|
23
|
+
Requires-Dist: python-dotenv>=1.0
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: networkx>=3.0
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
Requires-Dist: httpx>=0.25.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: black>=24.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
33
|
+
Provides-Extra: llm
|
|
34
|
+
Requires-Dist: pydantic-ai>=1.0; extra == "llm"
|
|
35
|
+
Provides-Extra: api
|
|
36
|
+
Requires-Dist: fastapi>=0.116.0; extra == "api"
|
|
37
|
+
Requires-Dist: uvicorn>=0.35.0; extra == "api"
|
|
38
|
+
Provides-Extra: benchmark
|
|
39
|
+
Requires-Dist: datasets<3.0.0,>=2.19.0; extra == "benchmark"
|
|
40
|
+
Provides-Extra: postgres
|
|
41
|
+
Requires-Dist: asyncpg>=0.29.0; extra == "postgres"
|
|
42
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
|
|
43
|
+
Provides-Extra: neo4j
|
|
44
|
+
Requires-Dist: neo4j>=5.0.0; extra == "neo4j"
|
|
45
|
+
Provides-Extra: all
|
|
46
|
+
Requires-Dist: grounded-memory[api,benchmark,dev,llm,neo4j,postgres]; extra == "all"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<img src="assets/gmem-logo.svg" alt="gmem" width="860" />
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
<p align="center">
|
|
54
|
+
<strong>gmem</strong> is a correctness-first memory runtime for LLM agents.<br/>
|
|
55
|
+
It stores only grounded facts, keeps temporal history, and stays adapter-driven instead of usecase-coupled.
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Why gmem
|
|
61
|
+
|
|
62
|
+
Most memory layers optimize retrieval speed first.
|
|
63
|
+
gmem optimizes correctness first:
|
|
64
|
+
|
|
65
|
+
- candidate tuples are proposed by the LLM
|
|
66
|
+
- constraints validate before persistence
|
|
67
|
+
- accepted facts become durable memory with supersession
|
|
68
|
+
- retrieval builds compact context from governed state
|
|
69
|
+
|
|
70
|
+
## Core Model
|
|
71
|
+
|
|
72
|
+
gmem keeps six objects explicit:
|
|
73
|
+
|
|
74
|
+
1. `Interaction` (immutable observed event)
|
|
75
|
+
2. `Entity` (symbolic anchor)
|
|
76
|
+
3. `CandidateFact` (untrusted proposal)
|
|
77
|
+
4. `ValidatedFact` (accepted fact with valid-time boundaries)
|
|
78
|
+
5. `Constraint` (write-time governance)
|
|
79
|
+
6. `AnswerContext` (ephemeral retrieval view)
|
|
80
|
+
|
|
81
|
+
## Quick Start
|
|
82
|
+
|
|
83
|
+
Install:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install -e .[llm]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Minimal usage:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from gmem import Memory
|
|
93
|
+
|
|
94
|
+
memory = Memory(adapter="generic", storage_backend="memory")
|
|
95
|
+
|
|
96
|
+
memory.add("My project codename is Atlas.", user_id="demo")
|
|
97
|
+
results = memory.search("What is my project codename?", user_id="demo", limit=3)
|
|
98
|
+
|
|
99
|
+
print(results)
|
|
100
|
+
memory.close()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Intent routing (auto-classify and route natural-language input):
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from gmem import Memory
|
|
107
|
+
|
|
108
|
+
memory = Memory(adapter="generic", storage_backend="memory")
|
|
109
|
+
|
|
110
|
+
# Explicit intent classification
|
|
111
|
+
intent = memory.route("What is my project codename?")
|
|
112
|
+
print(intent.action) # "recall"
|
|
113
|
+
|
|
114
|
+
# Auto-routing: REMEMBER -> add(), RECALL -> search(), etc.
|
|
115
|
+
result = memory.process("My project codename is Atlas.", user_id="demo")
|
|
116
|
+
print(result["intent"]["action"]) # "remember"
|
|
117
|
+
|
|
118
|
+
memory.close()
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## OpenRouter Setup
|
|
122
|
+
|
|
123
|
+
In `.env`:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
LLM_PROVIDER=openrouter
|
|
127
|
+
LLM_MODEL=z-ai/glm-4.5-air:free
|
|
128
|
+
OPENROUTER_API_KEY=...
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Quick API smoke check:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
make smoke-openrouter
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Local Dev UX
|
|
138
|
+
|
|
139
|
+
Start databases:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
make services-up
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Stop databases:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
make services-down
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Run memory smoke behavior test:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
make smoke-memory
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Run deterministic hybrid write + backend inspection (in-memory facts, Neo4j graph, PostgreSQL counts):
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
make inspect-backends
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Run the healthcare medication-reconciliation demo stack:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
make services-up
|
|
167
|
+
PYTHONPATH=src python demos/demo_bitemporal.py
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Then query the same data interactively (scope values match automatically):
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
PYTHONPATH=src python demos/demo_interactive.py --adapter healthcare
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
> **Scope alignment note:** Both demos use `require_scope=True` by default. Facts are tagged with scope fields (`tenant_id`, `app_id`, `user_id`, `agent_id`, `run_id`). To query data written by one demo from another, the scope values must match. The demos share default scope values so they align automatically; override with `--user-id`, `--agent-id`, `--run-id`, or env vars (`GM_SCOPE_*`) if needed.
|
|
177
|
+
|
|
178
|
+
Run the Postgres + Neo4j healthcare smoke check:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
make smoke-healthcare-backends
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Adapter-Decoupled Runtime
|
|
185
|
+
|
|
186
|
+
The runtime is no longer tied to repository `usecases/` trees.
|
|
187
|
+
|
|
188
|
+
- `GM_ADAPTER` selects behavior profile (default: `generic`)
|
|
189
|
+
- adapters are registered via runtime adapter registry APIs
|
|
190
|
+
- `domain_profile` remains as compatibility alias in the API
|
|
191
|
+
- experiment-stage labels for research ablations stay in the sibling [ClinicaLongMem-Interact](../ClinicaLongMem-Interact/) benchmark repository and docs, not as hardcoded runtime stage classes
|
|
192
|
+
|
|
193
|
+
## Engineering Workflow
|
|
194
|
+
|
|
195
|
+
- contribution guide: [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
196
|
+
- development conventions: [DEVELOPING.md](DEVELOPING.md)
|
|
197
|
+
|
|
198
|
+
## API Stability
|
|
199
|
+
|
|
200
|
+
Stable entry points:
|
|
201
|
+
|
|
202
|
+
- `gmem` facade imports
|
|
203
|
+
- `grounded_memory.memory.Memory` SDK facade
|
|
204
|
+
- FastAPI service endpoints under `grounded_memory.service`
|
|
205
|
+
|
|
206
|
+
Internal modules may evolve faster as research and storage internals iterate.
|
|
207
|
+
|
|
208
|
+
## Repository Layout
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
src/
|
|
212
|
+
gmem/ # facade package
|
|
213
|
+
grounded_memory/ # implementation runtime
|
|
214
|
+
adapters/ # adapter registry + generic agent
|
|
215
|
+
core/ # models, constraints, stores
|
|
216
|
+
llm/ # LLM client + extraction
|
|
217
|
+
retrieval/ # graph retrieval
|
|
218
|
+
service/ # FastAPI service layer
|
|
219
|
+
|
|
220
|
+
assets/ # brand and diagram assets
|
|
221
|
+
demos/ # runnable demos and showcase scripts
|
|
222
|
+
tests/ # regression tests
|
|
223
|
+
configs/ # runtime yaml configs
|
|
224
|
+
scripts/ # operational and migration scripts
|
|
225
|
+
docs/
|
|
226
|
+
architecture/ # architecture and roadmap docs
|
|
227
|
+
sdk/ # SDK reference docs
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Benchmark runners, scenarios, and evaluation scripts live in the sibling repository [ClinicaLongMem-Interact](../ClinicaLongMem-Interact/).
|
|
231
|
+
|
|
232
|
+
## Compose Stack
|
|
233
|
+
|
|
234
|
+
The compose stack is intentionally simple:
|
|
235
|
+
|
|
236
|
+
- PostgreSQL: source of truth store
|
|
237
|
+
- Neo4j: active graph projection
|
|
238
|
+
|
|
239
|
+
Configured via [docker-compose.yml](docker-compose.yml) and [.env.example](.env.example).
|
|
240
|
+
|
|
241
|
+
## Demos
|
|
242
|
+
|
|
243
|
+
| Demo | Command | What it shows |
|
|
244
|
+
|---|---|---|
|
|
245
|
+
| **Bitemporal medication reconciliation** | `python demos/demo_bitemporal.py` | Write-time constraints, supersession, discontinuation, current + historical retrieval |
|
|
246
|
+
| **Multi-patient write** | `python demos/demo_multi_patient_write.py` | Scale write-phase with 10 patients, allergy/interaction rejection |
|
|
247
|
+
| **Multi-patient retrieval** | `python demos/demo_multi_patient_retrieval.py` | Cross-patient isolation, shared entity queries, historical as-of |
|
|
248
|
+
| **Interactive REPL** | `python demos/demo_interactive.py --adapter healthcare` | Intent routing, auto-routing of natural language, real-time grounding diagnostics |
|
|
249
|
+
| **OpenRouter system** | `python demos/demo_openrouter.py` | Full OpenRouter pipeline with extraction, grounding, and retrieval |
|
|
250
|
+
|
|
251
|
+
## Tests
|
|
252
|
+
|
|
253
|
+
Run all regression tests:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
pytest tests/ -q
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Key test suites:
|
|
260
|
+
|
|
261
|
+
| Test file | Coverage |
|
|
262
|
+
|---|---|
|
|
263
|
+
| `tests/test_governance.py` | Supersession, duplicate detection, conflict resolution strategies, constraint enforcement |
|
|
264
|
+
| `tests/test_healthcare_reconciliation.py` | Allergy conflict, drug interaction, therapeutic duplication, dose supersession |
|
|
265
|
+
| `tests/test_healthcare_retrieval.py` | Retrieval planning, seed resolution, discontinuation closure, historical as-of, scope isolation |
|
|
266
|
+
| `tests/test_end_to_end.py` | Engineering knowledge graph, multi-domain facts, constraint rejection with audit trail |
|
|
267
|
+
| `tests/test_adapters.py` | Adapter registry, YAML constraint configs, entity/relation type coverage |
|
|
268
|
+
|
|
269
|
+
## Healthcare Demo
|
|
270
|
+
|
|
271
|
+
The demo uses the healthcare adapter with PostgreSQL as the durable
|
|
272
|
+
bitemporal store and Neo4j as the active graph projection:
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
Memory(
|
|
276
|
+
adapter="healthcare",
|
|
277
|
+
storage_backend="postgres_hybrid",
|
|
278
|
+
require_scope=True,
|
|
279
|
+
)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
It demonstrates LLM-backed clinical extraction, write-time safety constraints,
|
|
283
|
+
allergy/interaction rejections, dose supersession, discontinuation lifecycle
|
|
284
|
+
closure, current retrieval, and historical as-of retrieval.
|
|
285
|
+
|
|
286
|
+
## Academic Direction
|
|
287
|
+
|
|
288
|
+
The current roadmap emphasizes novelty around:
|
|
289
|
+
|
|
290
|
+
- bitemporal memory semantics for grounded tuples
|
|
291
|
+
- constraint lifecycle governance (`proposed -> shadow -> active -> deprecated`)
|
|
292
|
+
- adapter-level safety policies without domain hard-coding into core memory runtime
|
|
293
|
+
|
|
294
|
+
See [docs/architecture/ARCHITECTURE.md](docs/architecture/ARCHITECTURE.md) for
|
|
295
|
+
the implementation-aligned architecture and
|
|
296
|
+
[docs/architecture/GMEM_V2_BLUEPRINT.md](docs/architecture/GMEM_V2_BLUEPRINT.md)
|
|
297
|
+
for the broader roadmap.
|
|
298
|
+
|
|
299
|
+
## Status
|
|
300
|
+
|
|
301
|
+
This repository is under active demo development.
|
|
302
|
+
|
|
303
|
+
## License
|
|
304
|
+
|
|
305
|
+
MIT
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/gmem-logo.svg" alt="gmem" width="860" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>gmem</strong> is a correctness-first memory runtime for LLM agents.<br/>
|
|
7
|
+
It stores only grounded facts, keeps temporal history, and stays adapter-driven instead of usecase-coupled.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Why gmem
|
|
13
|
+
|
|
14
|
+
Most memory layers optimize retrieval speed first.
|
|
15
|
+
gmem optimizes correctness first:
|
|
16
|
+
|
|
17
|
+
- candidate tuples are proposed by the LLM
|
|
18
|
+
- constraints validate before persistence
|
|
19
|
+
- accepted facts become durable memory with supersession
|
|
20
|
+
- retrieval builds compact context from governed state
|
|
21
|
+
|
|
22
|
+
## Core Model
|
|
23
|
+
|
|
24
|
+
gmem keeps six objects explicit:
|
|
25
|
+
|
|
26
|
+
1. `Interaction` (immutable observed event)
|
|
27
|
+
2. `Entity` (symbolic anchor)
|
|
28
|
+
3. `CandidateFact` (untrusted proposal)
|
|
29
|
+
4. `ValidatedFact` (accepted fact with valid-time boundaries)
|
|
30
|
+
5. `Constraint` (write-time governance)
|
|
31
|
+
6. `AnswerContext` (ephemeral retrieval view)
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
Install:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install -e .[llm]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Minimal usage:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from gmem import Memory
|
|
45
|
+
|
|
46
|
+
memory = Memory(adapter="generic", storage_backend="memory")
|
|
47
|
+
|
|
48
|
+
memory.add("My project codename is Atlas.", user_id="demo")
|
|
49
|
+
results = memory.search("What is my project codename?", user_id="demo", limit=3)
|
|
50
|
+
|
|
51
|
+
print(results)
|
|
52
|
+
memory.close()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Intent routing (auto-classify and route natural-language input):
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from gmem import Memory
|
|
59
|
+
|
|
60
|
+
memory = Memory(adapter="generic", storage_backend="memory")
|
|
61
|
+
|
|
62
|
+
# Explicit intent classification
|
|
63
|
+
intent = memory.route("What is my project codename?")
|
|
64
|
+
print(intent.action) # "recall"
|
|
65
|
+
|
|
66
|
+
# Auto-routing: REMEMBER -> add(), RECALL -> search(), etc.
|
|
67
|
+
result = memory.process("My project codename is Atlas.", user_id="demo")
|
|
68
|
+
print(result["intent"]["action"]) # "remember"
|
|
69
|
+
|
|
70
|
+
memory.close()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## OpenRouter Setup
|
|
74
|
+
|
|
75
|
+
In `.env`:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
LLM_PROVIDER=openrouter
|
|
79
|
+
LLM_MODEL=z-ai/glm-4.5-air:free
|
|
80
|
+
OPENROUTER_API_KEY=...
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Quick API smoke check:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
make smoke-openrouter
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Local Dev UX
|
|
90
|
+
|
|
91
|
+
Start databases:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
make services-up
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Stop databases:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
make services-down
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Run memory smoke behavior test:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
make smoke-memory
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Run deterministic hybrid write + backend inspection (in-memory facts, Neo4j graph, PostgreSQL counts):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
make inspect-backends
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Run the healthcare medication-reconciliation demo stack:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
make services-up
|
|
119
|
+
PYTHONPATH=src python demos/demo_bitemporal.py
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Then query the same data interactively (scope values match automatically):
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
PYTHONPATH=src python demos/demo_interactive.py --adapter healthcare
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
> **Scope alignment note:** Both demos use `require_scope=True` by default. Facts are tagged with scope fields (`tenant_id`, `app_id`, `user_id`, `agent_id`, `run_id`). To query data written by one demo from another, the scope values must match. The demos share default scope values so they align automatically; override with `--user-id`, `--agent-id`, `--run-id`, or env vars (`GM_SCOPE_*`) if needed.
|
|
129
|
+
|
|
130
|
+
Run the Postgres + Neo4j healthcare smoke check:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
make smoke-healthcare-backends
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Adapter-Decoupled Runtime
|
|
137
|
+
|
|
138
|
+
The runtime is no longer tied to repository `usecases/` trees.
|
|
139
|
+
|
|
140
|
+
- `GM_ADAPTER` selects behavior profile (default: `generic`)
|
|
141
|
+
- adapters are registered via runtime adapter registry APIs
|
|
142
|
+
- `domain_profile` remains as compatibility alias in the API
|
|
143
|
+
- experiment-stage labels for research ablations stay in the sibling [ClinicaLongMem-Interact](../ClinicaLongMem-Interact/) benchmark repository and docs, not as hardcoded runtime stage classes
|
|
144
|
+
|
|
145
|
+
## Engineering Workflow
|
|
146
|
+
|
|
147
|
+
- contribution guide: [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
148
|
+
- development conventions: [DEVELOPING.md](DEVELOPING.md)
|
|
149
|
+
|
|
150
|
+
## API Stability
|
|
151
|
+
|
|
152
|
+
Stable entry points:
|
|
153
|
+
|
|
154
|
+
- `gmem` facade imports
|
|
155
|
+
- `grounded_memory.memory.Memory` SDK facade
|
|
156
|
+
- FastAPI service endpoints under `grounded_memory.service`
|
|
157
|
+
|
|
158
|
+
Internal modules may evolve faster as research and storage internals iterate.
|
|
159
|
+
|
|
160
|
+
## Repository Layout
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
src/
|
|
164
|
+
gmem/ # facade package
|
|
165
|
+
grounded_memory/ # implementation runtime
|
|
166
|
+
adapters/ # adapter registry + generic agent
|
|
167
|
+
core/ # models, constraints, stores
|
|
168
|
+
llm/ # LLM client + extraction
|
|
169
|
+
retrieval/ # graph retrieval
|
|
170
|
+
service/ # FastAPI service layer
|
|
171
|
+
|
|
172
|
+
assets/ # brand and diagram assets
|
|
173
|
+
demos/ # runnable demos and showcase scripts
|
|
174
|
+
tests/ # regression tests
|
|
175
|
+
configs/ # runtime yaml configs
|
|
176
|
+
scripts/ # operational and migration scripts
|
|
177
|
+
docs/
|
|
178
|
+
architecture/ # architecture and roadmap docs
|
|
179
|
+
sdk/ # SDK reference docs
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Benchmark runners, scenarios, and evaluation scripts live in the sibling repository [ClinicaLongMem-Interact](../ClinicaLongMem-Interact/).
|
|
183
|
+
|
|
184
|
+
## Compose Stack
|
|
185
|
+
|
|
186
|
+
The compose stack is intentionally simple:
|
|
187
|
+
|
|
188
|
+
- PostgreSQL: source of truth store
|
|
189
|
+
- Neo4j: active graph projection
|
|
190
|
+
|
|
191
|
+
Configured via [docker-compose.yml](docker-compose.yml) and [.env.example](.env.example).
|
|
192
|
+
|
|
193
|
+
## Demos
|
|
194
|
+
|
|
195
|
+
| Demo | Command | What it shows |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| **Bitemporal medication reconciliation** | `python demos/demo_bitemporal.py` | Write-time constraints, supersession, discontinuation, current + historical retrieval |
|
|
198
|
+
| **Multi-patient write** | `python demos/demo_multi_patient_write.py` | Scale write-phase with 10 patients, allergy/interaction rejection |
|
|
199
|
+
| **Multi-patient retrieval** | `python demos/demo_multi_patient_retrieval.py` | Cross-patient isolation, shared entity queries, historical as-of |
|
|
200
|
+
| **Interactive REPL** | `python demos/demo_interactive.py --adapter healthcare` | Intent routing, auto-routing of natural language, real-time grounding diagnostics |
|
|
201
|
+
| **OpenRouter system** | `python demos/demo_openrouter.py` | Full OpenRouter pipeline with extraction, grounding, and retrieval |
|
|
202
|
+
|
|
203
|
+
## Tests
|
|
204
|
+
|
|
205
|
+
Run all regression tests:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
pytest tests/ -q
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Key test suites:
|
|
212
|
+
|
|
213
|
+
| Test file | Coverage |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `tests/test_governance.py` | Supersession, duplicate detection, conflict resolution strategies, constraint enforcement |
|
|
216
|
+
| `tests/test_healthcare_reconciliation.py` | Allergy conflict, drug interaction, therapeutic duplication, dose supersession |
|
|
217
|
+
| `tests/test_healthcare_retrieval.py` | Retrieval planning, seed resolution, discontinuation closure, historical as-of, scope isolation |
|
|
218
|
+
| `tests/test_end_to_end.py` | Engineering knowledge graph, multi-domain facts, constraint rejection with audit trail |
|
|
219
|
+
| `tests/test_adapters.py` | Adapter registry, YAML constraint configs, entity/relation type coverage |
|
|
220
|
+
|
|
221
|
+
## Healthcare Demo
|
|
222
|
+
|
|
223
|
+
The demo uses the healthcare adapter with PostgreSQL as the durable
|
|
224
|
+
bitemporal store and Neo4j as the active graph projection:
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
Memory(
|
|
228
|
+
adapter="healthcare",
|
|
229
|
+
storage_backend="postgres_hybrid",
|
|
230
|
+
require_scope=True,
|
|
231
|
+
)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
It demonstrates LLM-backed clinical extraction, write-time safety constraints,
|
|
235
|
+
allergy/interaction rejections, dose supersession, discontinuation lifecycle
|
|
236
|
+
closure, current retrieval, and historical as-of retrieval.
|
|
237
|
+
|
|
238
|
+
## Academic Direction
|
|
239
|
+
|
|
240
|
+
The current roadmap emphasizes novelty around:
|
|
241
|
+
|
|
242
|
+
- bitemporal memory semantics for grounded tuples
|
|
243
|
+
- constraint lifecycle governance (`proposed -> shadow -> active -> deprecated`)
|
|
244
|
+
- adapter-level safety policies without domain hard-coding into core memory runtime
|
|
245
|
+
|
|
246
|
+
See [docs/architecture/ARCHITECTURE.md](docs/architecture/ARCHITECTURE.md) for
|
|
247
|
+
the implementation-aligned architecture and
|
|
248
|
+
[docs/architecture/GMEM_V2_BLUEPRINT.md](docs/architecture/GMEM_V2_BLUEPRINT.md)
|
|
249
|
+
for the broader roadmap.
|
|
250
|
+
|
|
251
|
+
## Status
|
|
252
|
+
|
|
253
|
+
This repository is under active demo development.
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "grounded-memory"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Correctness-First Structured Memory Layer for LLM Agents"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [
|
|
9
|
+
{name = "Faycal & Marouane"}
|
|
10
|
+
]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
dependencies = [
|
|
24
|
+
"pydantic>=2.0",
|
|
25
|
+
"typing-extensions>=4.14.1",
|
|
26
|
+
"python-dateutil>=2.8",
|
|
27
|
+
"python-dotenv>=1.0",
|
|
28
|
+
"pyyaml>=6.0",
|
|
29
|
+
"networkx>=3.0",
|
|
30
|
+
"rich>=13.0",
|
|
31
|
+
"httpx>=0.25.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Repository = "https://github.com/marouaneoa/GroundedMemory"
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = [
|
|
39
|
+
"black>=24.0",
|
|
40
|
+
"pytest>=7.0",
|
|
41
|
+
"pytest-asyncio>=0.21",
|
|
42
|
+
"ruff>=0.6.0",
|
|
43
|
+
]
|
|
44
|
+
llm = [
|
|
45
|
+
"pydantic-ai>=1.0",
|
|
46
|
+
]
|
|
47
|
+
api = [
|
|
48
|
+
"fastapi>=0.116.0",
|
|
49
|
+
"uvicorn>=0.35.0",
|
|
50
|
+
]
|
|
51
|
+
benchmark = [
|
|
52
|
+
"datasets>=2.19.0,<3.0.0",
|
|
53
|
+
]
|
|
54
|
+
postgres = [
|
|
55
|
+
"asyncpg>=0.29.0",
|
|
56
|
+
"psycopg2-binary>=2.9.0",
|
|
57
|
+
]
|
|
58
|
+
neo4j = [
|
|
59
|
+
"neo4j>=5.0.0",
|
|
60
|
+
]
|
|
61
|
+
all = [
|
|
62
|
+
"grounded-memory[dev,llm,api,benchmark,postgres,neo4j]",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[build-system]
|
|
66
|
+
requires = ["setuptools>=61.0"]
|
|
67
|
+
build-backend = "setuptools.build_meta"
|
|
68
|
+
|
|
69
|
+
[tool.setuptools.packages.find]
|
|
70
|
+
where = ["src"]
|
|
71
|
+
|
|
72
|
+
[tool.setuptools.package-data]
|
|
73
|
+
"grounded_memory.configs" = ["*.yaml"]
|
|
74
|
+
|
|
75
|
+
[tool.black]
|
|
76
|
+
line-length = 100
|
|
77
|
+
target-version = ["py310"]
|
|
78
|
+
|
|
79
|
+
[tool.ruff]
|
|
80
|
+
line-length = 100
|
|
81
|
+
target-version = "py310"
|
|
82
|
+
src = ["src", "tests", "demos", "scripts"]
|
|
83
|
+
|
|
84
|
+
[tool.pytest.ini_options]
|
|
85
|
+
testpaths = ["tests"]
|
|
86
|
+
pythonpath = ["src"]
|
|
87
|
+
|
|
88
|
+
[tool.ruff.lint]
|
|
89
|
+
select = [
|
|
90
|
+
"E",
|
|
91
|
+
"F",
|
|
92
|
+
"I",
|
|
93
|
+
"B",
|
|
94
|
+
"UP",
|
|
95
|
+
"SIM",
|
|
96
|
+
]
|
|
97
|
+
ignore = [
|
|
98
|
+
"E501",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
[tool.ruff.lint.isort]
|
|
102
|
+
known-first-party = ["grounded_memory", "gmem"]
|
|
103
|
+
|
|
104
|
+
[tool.ruff.format]
|
|
105
|
+
quote-style = "double"
|
|
106
|
+
indent-style = "space"
|