pycontextdb 0.3.2__tar.gz → 0.4.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 (152) hide show
  1. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/.github/workflows/ci.yml +2 -0
  2. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/CHANGELOG.md +12 -0
  3. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/PKG-INFO +41 -4
  4. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/README.md +40 -3
  5. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/__init__.py +17 -1
  6. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/client.py +448 -0
  7. pycontextdb-0.4.0/contextdb/connectors/__init__.py +25 -0
  8. pycontextdb-0.4.0/contextdb/connectors/base.py +62 -0
  9. pycontextdb-0.4.0/contextdb/connectors/postgres.py +243 -0
  10. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/core/__init__.py +18 -0
  11. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/core/exceptions.py +25 -0
  12. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/core/models.py +47 -1
  13. pycontextdb-0.4.0/contextdb/dynamics/trust.py +784 -0
  14. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/memory/factual.py +37 -1
  15. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/store/postgres_store.py +4 -1
  16. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/store/sqlite_store.py +4 -1
  17. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/store/vector_index.py +14 -0
  18. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/api.md +20 -3
  19. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/architecture.md +14 -4
  20. pycontextdb-0.4.0/docs/connectors.md +56 -0
  21. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/index.md +7 -0
  22. pycontextdb-0.4.0/docs/memory_evolution.md +124 -0
  23. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/pyproject.toml +1 -1
  24. pycontextdb-0.4.0/tests/unit/test_connectors.py +47 -0
  25. pycontextdb-0.4.0/tests/unit/test_memory_evolution.py +456 -0
  26. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_models.py +40 -0
  27. pycontextdb-0.4.0/tests/unit/test_postgres_connector.py +80 -0
  28. pycontextdb-0.4.0/tests/unit/test_postgres_memory_evolution.py +411 -0
  29. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_vector_index.py +24 -0
  30. pycontextdb-0.3.2/contextdb/dynamics/trust.py +0 -373
  31. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/.github/CODEOWNERS +0 -0
  32. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/.github/copilot-instructions.md +0 -0
  33. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/.github/pull_request_template.md +0 -0
  34. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/.github/workflows/publish.yml +0 -0
  35. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/.gitignore +0 -0
  36. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/.pre-commit-config.yaml +0 -0
  37. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/COMMERCIAL.md +0 -0
  38. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/CONTRIBUTING.md +0 -0
  39. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/LICENSE +0 -0
  40. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/NOTICE +0 -0
  41. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/OPEN_CORE.md +0 -0
  42. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/ai-tools/.cursorrules +0 -0
  43. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/ai-tools/CLAUDE.md +0 -0
  44. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/ai-tools/README.md +0 -0
  45. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/ai-tools/llms-full.txt +0 -0
  46. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/ai-tools/llms.txt +0 -0
  47. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/assets/README.md +0 -0
  48. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/assets/architecture.png +0 -0
  49. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/assets/benchmarks.png +0 -0
  50. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/assets/hero.png +0 -0
  51. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/assets/performance.png +0 -0
  52. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/assets/problem.png +0 -0
  53. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/assets/quickstart.png +0 -0
  54. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/benchmarks/__init__.py +0 -0
  55. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/benchmarks/run_benchmarks.py +0 -0
  56. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/benchmarks/trust_bakeoff.py +0 -0
  57. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/agents/__init__.py +0 -0
  58. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/agents/memory_bus.py +0 -0
  59. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/agents/rl_manager.py +0 -0
  60. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/cli.py +0 -0
  61. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/core/clock.py +0 -0
  62. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/core/config.py +0 -0
  63. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/core/policy.py +0 -0
  64. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/core/slots.py +0 -0
  65. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/dynamics/__init__.py +0 -0
  66. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/dynamics/evolution.py +0 -0
  67. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/dynamics/formation.py +0 -0
  68. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/dynamics/retrieval.py +0 -0
  69. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/dynamics/salience.py +0 -0
  70. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/graphs/__init__.py +0 -0
  71. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/graphs/base.py +0 -0
  72. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/graphs/causal.py +0 -0
  73. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/graphs/entity.py +0 -0
  74. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/graphs/semantic.py +0 -0
  75. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/graphs/temporal.py +0 -0
  76. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/__init__.py +0 -0
  77. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/act.py +0 -0
  78. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/autogen.py +0 -0
  79. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/crewai.py +0 -0
  80. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/langchain.py +0 -0
  81. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/livekit.py +0 -0
  82. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/openai_tools.py +0 -0
  83. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/pipecat.py +0 -0
  84. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/integrations/prompting.py +0 -0
  85. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/mcp.py +0 -0
  86. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/memory/__init__.py +0 -0
  87. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/memory/experiential.py +0 -0
  88. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/memory/working.py +0 -0
  89. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/pool.py +0 -0
  90. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/privacy/__init__.py +0 -0
  91. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/privacy/audit.py +0 -0
  92. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/privacy/injection.py +0 -0
  93. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/privacy/pii_detector.py +0 -0
  94. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/privacy/retention.py +0 -0
  95. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/py.typed +0 -0
  96. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/serve.py +0 -0
  97. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/store/__init__.py +0 -0
  98. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/store/base.py +0 -0
  99. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/store/factory.py +0 -0
  100. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/store/pg_sql.py +0 -0
  101. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/utils/__init__.py +0 -0
  102. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/utils/embeddings.py +0 -0
  103. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/utils/llm.py +0 -0
  104. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/contextdb/utils/migrations.py +0 -0
  105. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/migrations.md +0 -0
  106. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/multi_tenant.md +0 -0
  107. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/postgres.md +0 -0
  108. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/prompts.md +0 -0
  109. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/quickstart.md +0 -0
  110. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/serve.md +0 -0
  111. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/docs/trust_policy.md +0 -0
  112. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/examples/customer_support.py +0 -0
  113. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/examples/multi_agent_team.py +0 -0
  114. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/examples/phone_agent.py +0 -0
  115. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/examples/research_assistant.py +0 -0
  116. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/legal/CLA-entity.md +0 -0
  117. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/legal/CLA-individual.md +0 -0
  118. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/legal/signed/README.md +0 -0
  119. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/mkdocs.yml +0 -0
  120. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/scripts/check_open_core_boundary.py +0 -0
  121. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/scripts/check_release.py +0 -0
  122. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/__init__.py +0 -0
  123. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/conftest.py +0 -0
  124. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/evals/__init__.py +0 -0
  125. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/evals/test_trust_model.py +0 -0
  126. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/integration/__init__.py +0 -0
  127. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/integration/test_client.py +0 -0
  128. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/integration/test_langchain_integration.py +0 -0
  129. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/pg_util.py +0 -0
  130. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/test_version.py +0 -0
  131. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/__init__.py +0 -0
  132. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_audit.py +0 -0
  133. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_config.py +0 -0
  134. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_decision_scope.py +0 -0
  135. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_embeddings.py +0 -0
  136. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_evolution.py +0 -0
  137. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_formation.py +0 -0
  138. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_graphs.py +0 -0
  139. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_host_api.py +0 -0
  140. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_init.py +0 -0
  141. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_memory_bus.py +0 -0
  142. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_open_core_boundary.py +0 -0
  143. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_pg_sql.py +0 -0
  144. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_pii_detector.py +0 -0
  145. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_postgres_concurrency.py +0 -0
  146. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_retention.py +0 -0
  147. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_retrieval.py +0 -0
  148. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_scoped_id_access.py +0 -0
  149. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_serve.py +0 -0
  150. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_store.py +0 -0
  151. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_trust_policy.py +0 -0
  152. {pycontextdb-0.3.2 → pycontextdb-0.4.0}/tests/unit/test_vector_scope.py +0 -0
@@ -105,5 +105,7 @@ jobs:
105
105
  run: |
106
106
  pytest \
107
107
  tests/unit/test_postgres_concurrency.py \
108
+ tests/unit/test_postgres_connector.py \
109
+ tests/unit/test_postgres_memory_evolution.py \
108
110
  tests/unit/test_scoped_id_access.py \
109
111
  tests/unit/test_vector_scope.py
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.4.0 — 2026-08-23
6
+
7
+ - Explicit deterministic `add` / `update` / `delete` / `noop` memory
8
+ evolution with public result and error contracts, lineage, scoped hard
9
+ deletion, content-free NOOP audit, and post-commit consistency tokens.
10
+ Postgres applies memory rows, revisions, and audit in one transaction;
11
+ ordinary `factual.add()` remains compatible and same-speaker duplicates
12
+ are now true no-ops.
13
+ - Public connector contracts plus a minimal local PostgreSQL/Supabase reader
14
+ with stable timestamp-plus-key cursors and soft-delete mapping. Hosts retain
15
+ control of credentials, scheduling, retries, and memory writes.
16
+
5
17
  ## 0.3.2 — 2026-08-20
6
18
 
7
19
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pycontextdb
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: The unified context layer for AI agents — memory you can act on without treating wishes as facts
5
5
  Project-URL: Homepage, https://github.com/atomsai/contextdb
6
6
  Project-URL: Documentation, https://contextdb.dev
@@ -74,7 +74,7 @@ Description-Content-Type: text/markdown
74
74
  <p align="center">
75
75
  <a href="https://pypi.org/project/pycontextdb/"><img src="https://img.shields.io/pypi/v/pycontextdb.svg" alt="PyPI version"></a>
76
76
  <a href="https://github.com/atomsai/contextdb/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License: Apache 2.0"></a>
77
- <a href="https://github.com/atomsai/contextdb/blob/main/tests/"><img src="https://img.shields.io/badge/tests-139%20passing-brightgreen.svg" alt="Tests"></a>
77
+ <a href="https://github.com/atomsai/contextdb/blob/main/tests/"><img src="https://img.shields.io/badge/tests-195%20passing-brightgreen.svg" alt="Tests"></a>
78
78
  <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python 3.10+"></a>
79
79
  <a href="https://github.com/atomsai/contextdb/blob/main/pyproject.toml"><img src="https://img.shields.io/badge/mypy-strict-blue.svg" alt="Type Checked"></a>
80
80
  <a href="https://github.com/atomsai/contextdb/blob/main/benchmarks/run_benchmarks.py"><img src="https://img.shields.io/badge/search_p95-%3C5ms_%40_5K-brightgreen.svg" alt="Search p95"></a>
@@ -102,7 +102,7 @@ Description-Content-Type: text/markdown
102
102
  | **Search latency (5K memories)** | p50 **3.9ms** · p95 **5.0ms** |
103
103
  | **Vector search (10K × 1,536d)** | p50 **0.8ms** · p95 **1.0ms** |
104
104
  | **PII detection** | 100,000+ texts/sec |
105
- | **Tests** | 139 passing (incl. 34 trust-model acceptance evals) · ruff clean · mypy `--strict` clean |
105
+ | **Tests** | 195 passing (incl. 34 trust-model acceptance evals) · ruff clean · mypy `--strict` clean |
106
106
  | **Dependencies** | SQLite + NumPy (FAISS / Postgres optional) |
107
107
 
108
108
  Those search numbers are **SQLite + the in-repo mock embedder**. A recall
@@ -155,7 +155,7 @@ Databricks Lakebase gives agents a hard drive. ContextDB gives agents a brain.
155
155
  | PostgreSQL / MongoDB | Static rows; no lifecycle; no graph links | `FactualMemory` with formation, evolution, and consolidation |
156
156
  | S3 / flat files | Write-only archive; not queryable by meaning or time | `ExperientialMemory` for trajectories, reflections, and workflows |
157
157
  | Custom glue code | Brittle; rebuilt at every company; 2-4 engineering months | One `init()` call, one async SDK, one dependency |
158
- | *(nothing today)* | Agents never learn from outcomes | RL-trained memory manager (`ADD` / `UPDATE` / `DELETE` / `NOOP`) |
158
+ | Ad hoc update/delete glue | Corrections overwrite history; deletion leaves vector residue | Deterministic `evolve()` operations with lineage, audit, and consistency results |
159
159
  | *(nothing today)* | Raw PII stored indefinitely; compliance risk | PII detection, typed TTLs, hash-chained audit log |
160
160
 
161
161
  ---
@@ -217,6 +217,43 @@ async def main() -> None:
217
217
  asyncio.run(main())
218
218
  ```
219
219
 
220
+ ### Explicit memory evolution
221
+
222
+ When the caller already knows the operation, apply it directly:
223
+
224
+ ```python
225
+ current = await db.factual.evolve(
226
+ "add",
227
+ "Customer prefers email.",
228
+ source="user_stated",
229
+ entity="user",
230
+ attribute="contact_preference",
231
+ )
232
+ assert current.memory is not None
233
+ result = await db.factual.evolve(
234
+ "update",
235
+ "Customer prefers SMS, not email.",
236
+ source="user_stated",
237
+ target_memory_id=current.memory.id,
238
+ )
239
+
240
+ print(result.previous_memory_ids)
241
+ print(result.memory.id)
242
+ print(result.consistency_token.memory_version)
243
+ ```
244
+
245
+ `evolve()` supports `add`, `update`, `delete`, and `noop`. Corrections create a
246
+ successor and close the predecessor; they do not overwrite corrected content
247
+ in place. Delete hard-removes the selected row and vector. Same-speaker
248
+ duplicates are true no-ops and do not advance the memory version. The caller
249
+ selects the operation; this path does not use an LLM planner or the optional RL
250
+ manager.
251
+
252
+ See the
253
+ [explicit evolution contract](https://github.com/atomsai/contextdb/blob/main/docs/memory_evolution.md)
254
+ for conflict, contested-slot, audit, scoping, and Postgres transaction
255
+ semantics.
256
+
220
257
  ---
221
258
 
222
259
  ## Experiential memory
@@ -8,7 +8,7 @@
8
8
  <p align="center">
9
9
  <a href="https://pypi.org/project/pycontextdb/"><img src="https://img.shields.io/pypi/v/pycontextdb.svg" alt="PyPI version"></a>
10
10
  <a href="https://github.com/atomsai/contextdb/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License: Apache 2.0"></a>
11
- <a href="https://github.com/atomsai/contextdb/blob/main/tests/"><img src="https://img.shields.io/badge/tests-139%20passing-brightgreen.svg" alt="Tests"></a>
11
+ <a href="https://github.com/atomsai/contextdb/blob/main/tests/"><img src="https://img.shields.io/badge/tests-195%20passing-brightgreen.svg" alt="Tests"></a>
12
12
  <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python 3.10+"></a>
13
13
  <a href="https://github.com/atomsai/contextdb/blob/main/pyproject.toml"><img src="https://img.shields.io/badge/mypy-strict-blue.svg" alt="Type Checked"></a>
14
14
  <a href="https://github.com/atomsai/contextdb/blob/main/benchmarks/run_benchmarks.py"><img src="https://img.shields.io/badge/search_p95-%3C5ms_%40_5K-brightgreen.svg" alt="Search p95"></a>
@@ -36,7 +36,7 @@
36
36
  | **Search latency (5K memories)** | p50 **3.9ms** · p95 **5.0ms** |
37
37
  | **Vector search (10K × 1,536d)** | p50 **0.8ms** · p95 **1.0ms** |
38
38
  | **PII detection** | 100,000+ texts/sec |
39
- | **Tests** | 139 passing (incl. 34 trust-model acceptance evals) · ruff clean · mypy `--strict` clean |
39
+ | **Tests** | 195 passing (incl. 34 trust-model acceptance evals) · ruff clean · mypy `--strict` clean |
40
40
  | **Dependencies** | SQLite + NumPy (FAISS / Postgres optional) |
41
41
 
42
42
  Those search numbers are **SQLite + the in-repo mock embedder**. A recall
@@ -89,7 +89,7 @@ Databricks Lakebase gives agents a hard drive. ContextDB gives agents a brain.
89
89
  | PostgreSQL / MongoDB | Static rows; no lifecycle; no graph links | `FactualMemory` with formation, evolution, and consolidation |
90
90
  | S3 / flat files | Write-only archive; not queryable by meaning or time | `ExperientialMemory` for trajectories, reflections, and workflows |
91
91
  | Custom glue code | Brittle; rebuilt at every company; 2-4 engineering months | One `init()` call, one async SDK, one dependency |
92
- | *(nothing today)* | Agents never learn from outcomes | RL-trained memory manager (`ADD` / `UPDATE` / `DELETE` / `NOOP`) |
92
+ | Ad hoc update/delete glue | Corrections overwrite history; deletion leaves vector residue | Deterministic `evolve()` operations with lineage, audit, and consistency results |
93
93
  | *(nothing today)* | Raw PII stored indefinitely; compliance risk | PII detection, typed TTLs, hash-chained audit log |
94
94
 
95
95
  ---
@@ -151,6 +151,43 @@ async def main() -> None:
151
151
  asyncio.run(main())
152
152
  ```
153
153
 
154
+ ### Explicit memory evolution
155
+
156
+ When the caller already knows the operation, apply it directly:
157
+
158
+ ```python
159
+ current = await db.factual.evolve(
160
+ "add",
161
+ "Customer prefers email.",
162
+ source="user_stated",
163
+ entity="user",
164
+ attribute="contact_preference",
165
+ )
166
+ assert current.memory is not None
167
+ result = await db.factual.evolve(
168
+ "update",
169
+ "Customer prefers SMS, not email.",
170
+ source="user_stated",
171
+ target_memory_id=current.memory.id,
172
+ )
173
+
174
+ print(result.previous_memory_ids)
175
+ print(result.memory.id)
176
+ print(result.consistency_token.memory_version)
177
+ ```
178
+
179
+ `evolve()` supports `add`, `update`, `delete`, and `noop`. Corrections create a
180
+ successor and close the predecessor; they do not overwrite corrected content
181
+ in place. Delete hard-removes the selected row and vector. Same-speaker
182
+ duplicates are true no-ops and do not advance the memory version. The caller
183
+ selects the operation; this path does not use an LLM planner or the optional RL
184
+ manager.
185
+
186
+ See the
187
+ [explicit evolution contract](https://github.com/atomsai/contextdb/blob/main/docs/memory_evolution.md)
188
+ for conflict, contested-slot, audit, scoping, and Postgres transaction
189
+ semantics.
190
+
154
191
  ---
155
192
 
156
193
  ## Experiential memory
@@ -10,6 +10,11 @@ from contextdb.core.config import ContextDBConfig
10
10
  from contextdb.core.exceptions import (
11
11
  ConfigError,
12
12
  ContextDBError,
13
+ EvolutionConflictError,
14
+ EvolutionOperationConflictError,
15
+ EvolutionTargetNotFoundError,
16
+ EvolutionTargetRequiredError,
17
+ MemoryEvolutionError,
13
18
  MemoryNotFoundError,
14
19
  PrivacyError,
15
20
  ScopeConflictError,
@@ -21,7 +26,10 @@ from contextdb.core.exceptions import (
21
26
  from contextdb.core.models import (
22
27
  Edge,
23
28
  Entity,
29
+ EvolutionOperation,
30
+ EvolutionOutcome,
24
31
  MemoryConsistencyToken,
32
+ MemoryEvolutionResult,
25
33
  MemoryItem,
26
34
  MemoryStatus,
27
35
  MemoryType,
@@ -33,7 +41,7 @@ from contextdb.core.policy import TrustPolicy
33
41
  from contextdb.pool import ContextDBPool
34
42
  from contextdb.utils.embeddings import EmbeddingProvider
35
43
 
36
- __version__ = "0.3.2"
44
+ __version__ = "0.4.0"
37
45
 
38
46
  __all__ = [
39
47
  "Clock",
@@ -45,7 +53,15 @@ __all__ = [
45
53
  "Edge",
46
54
  "EmbeddingProvider",
47
55
  "Entity",
56
+ "EvolutionConflictError",
57
+ "EvolutionOperationConflictError",
58
+ "EvolutionOperation",
59
+ "EvolutionOutcome",
60
+ "EvolutionTargetNotFoundError",
61
+ "EvolutionTargetRequiredError",
48
62
  "FrozenClock",
63
+ "MemoryEvolutionError",
64
+ "MemoryEvolutionResult",
49
65
  "MemoryItem",
50
66
  "MemoryConsistencyToken",
51
67
  "MemoryNotFoundError",