pycontextdb 0.4.6__tar.gz → 0.4.8__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.4.6 → pycontextdb-0.4.8}/CHANGELOG.md +52 -0
  2. pycontextdb-0.4.8/COMMERCIAL.md +71 -0
  3. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/PKG-INFO +43 -50
  4. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/README.md +42 -49
  5. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/__init__.py +1 -1
  6. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/client.py +85 -8
  7. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/config.py +13 -3
  8. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/policy.py +6 -4
  9. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/slots.py +49 -3
  10. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/retrieval.py +187 -4
  11. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/trust.py +12 -4
  12. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/entity.py +118 -12
  13. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/prompting.py +30 -7
  14. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/memory/factual.py +4 -0
  15. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/pii_detector.py +40 -5
  16. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/base.py +19 -0
  17. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/postgres_store.py +61 -6
  18. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/sqlite_store.py +52 -8
  19. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/index.md +8 -4
  20. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/prompts.md +1 -1
  21. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/quickstart.md +3 -1
  22. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/pyproject.toml +1 -1
  23. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/evals/test_trust_model.py +193 -0
  24. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_pii_detector.py +33 -0
  25. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_retrieval.py +73 -0
  26. pycontextdb-0.4.6/COMMERCIAL.md +0 -44
  27. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/CODEOWNERS +0 -0
  28. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/copilot-instructions.md +0 -0
  29. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/pull_request_template.md +0 -0
  30. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/workflows/ci.yml +0 -0
  31. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/workflows/publish.yml +0 -0
  32. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.gitignore +0 -0
  33. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.pre-commit-config.yaml +0 -0
  34. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/CONTRIBUTING.md +0 -0
  35. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/LICENSE +0 -0
  36. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/NOTICE +0 -0
  37. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/OPEN_CORE.md +0 -0
  38. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/.cursorrules +0 -0
  39. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/CLAUDE.md +0 -0
  40. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/README.md +0 -0
  41. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/llms-full.txt +0 -0
  42. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/llms.txt +0 -0
  43. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/README.md +0 -0
  44. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/architecture.png +0 -0
  45. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/benchmarks.png +0 -0
  46. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/hero.png +0 -0
  47. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/performance.png +0 -0
  48. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/problem.png +0 -0
  49. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/quickstart.png +0 -0
  50. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/benchmarks/__init__.py +0 -0
  51. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/benchmarks/run_benchmarks.py +0 -0
  52. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/benchmarks/trust_bakeoff.py +0 -0
  53. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/agents/__init__.py +0 -0
  54. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/agents/memory_bus.py +0 -0
  55. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/agents/rl_manager.py +0 -0
  56. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/cli.py +0 -0
  57. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/connectors/__init__.py +0 -0
  58. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/connectors/base.py +0 -0
  59. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/connectors/postgres.py +0 -0
  60. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/__init__.py +0 -0
  61. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/clock.py +0 -0
  62. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/exceptions.py +0 -0
  63. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/models.py +0 -0
  64. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/__init__.py +0 -0
  65. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/evolution.py +0 -0
  66. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/formation.py +0 -0
  67. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/salience.py +0 -0
  68. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/__init__.py +0 -0
  69. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/base.py +0 -0
  70. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/causal.py +0 -0
  71. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/semantic.py +0 -0
  72. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/temporal.py +0 -0
  73. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/__init__.py +0 -0
  74. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/act.py +0 -0
  75. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/autogen.py +0 -0
  76. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/crewai.py +0 -0
  77. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/langchain.py +0 -0
  78. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/livekit.py +0 -0
  79. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/openai_tools.py +0 -0
  80. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/pipecat.py +0 -0
  81. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/mcp.py +0 -0
  82. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/memory/__init__.py +0 -0
  83. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/memory/experiential.py +0 -0
  84. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/memory/working.py +0 -0
  85. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/pool.py +0 -0
  86. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/__init__.py +0 -0
  87. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/audit.py +0 -0
  88. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/injection.py +0 -0
  89. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/retention.py +0 -0
  90. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/py.typed +0 -0
  91. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/serve.py +0 -0
  92. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/__init__.py +0 -0
  93. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/factory.py +0 -0
  94. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/pg_sql.py +0 -0
  95. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/vector_index.py +0 -0
  96. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/utils/__init__.py +0 -0
  97. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/utils/embeddings.py +0 -0
  98. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/utils/llm.py +0 -0
  99. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/utils/migrations.py +0 -0
  100. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/api.md +0 -0
  101. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/architecture.md +0 -0
  102. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/connectors.md +0 -0
  103. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/memory_evolution.md +0 -0
  104. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/migrations.md +0 -0
  105. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/multi_tenant.md +0 -0
  106. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/postgres.md +0 -0
  107. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/serve.md +0 -0
  108. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/trust_policy.md +0 -0
  109. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/examples/customer_support.py +0 -0
  110. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/examples/multi_agent_team.py +0 -0
  111. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/examples/phone_agent.py +0 -0
  112. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/examples/research_assistant.py +0 -0
  113. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/legal/CLA-entity.md +0 -0
  114. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/legal/CLA-individual.md +0 -0
  115. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/legal/signed/README.md +0 -0
  116. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/mkdocs.yml +0 -0
  117. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/scripts/check_open_core_boundary.py +0 -0
  118. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/scripts/check_release.py +0 -0
  119. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/__init__.py +0 -0
  120. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/conftest.py +0 -0
  121. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/evals/__init__.py +0 -0
  122. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/integration/__init__.py +0 -0
  123. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/integration/test_client.py +0 -0
  124. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/integration/test_langchain_integration.py +0 -0
  125. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/pg_util.py +0 -0
  126. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/test_version.py +0 -0
  127. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/__init__.py +0 -0
  128. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_audit.py +0 -0
  129. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_config.py +0 -0
  130. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_connectors.py +0 -0
  131. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_decision_scope.py +0 -0
  132. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_embeddings.py +0 -0
  133. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_evolution.py +0 -0
  134. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_formation.py +0 -0
  135. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_graphs.py +0 -0
  136. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_host_api.py +0 -0
  137. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_init.py +0 -0
  138. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_memory_bus.py +0 -0
  139. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_memory_evolution.py +0 -0
  140. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_models.py +0 -0
  141. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_open_core_boundary.py +0 -0
  142. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_pg_sql.py +0 -0
  143. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_postgres_concurrency.py +0 -0
  144. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_postgres_connector.py +0 -0
  145. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_postgres_memory_evolution.py +0 -0
  146. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_retention.py +0 -0
  147. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_scoped_id_access.py +0 -0
  148. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_serve.py +0 -0
  149. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_store.py +0 -0
  150. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_trust_policy.py +0 -0
  151. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_vector_index.py +0 -0
  152. {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_vector_scope.py +0 -0
@@ -2,6 +2,58 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.4.8 — 2026-10-21
6
+
7
+ - `factual.add()`, `add_fast()`, and `add_many()` accept optional `event_time`
8
+ and set `valid_from = event_time` (closing superseded slot occupants at
9
+ `valid_until = event_time`) so backdated writes carry their real validity
10
+ window.
11
+ - `RetrievalEngine.search_scored()` applies `as_of` filtering in `SQLiteStore`
12
+ and `PostgresStore` before top-k truncation and computes salience decay
13
+ relative to `as_of`.
14
+ - `RetrievalEngine.search_scored()` adds a general BM25 keyword ranking arm
15
+ (`_bm25_ranking`) fused via Reciprocal Rank Fusion alongside semantic and
16
+ graph rankings, while keeping `recall_for_action` relevance-floor bypass
17
+ restricted to digit-bearing `identifier_tokens`.
18
+ - `render_recalled_context()` adds optional `include_timestamps` and
19
+ `chronological` ordering and marks `CONTESTED` memories.
20
+
21
+ ## 0.4.7 — 2026-10-12
22
+
23
+ - `forget_user` now removes what the user added to shared entities. Entity
24
+ attributes record the memory that supplied them, are rebuilt from the
25
+ remaining sources when a memory is removed, and an entity no memory still
26
+ mentions is deleted. `verify_forgotten` fails if an edge or attribute source
27
+ still points at a deleted memory. Attributes merged before 0.4.7 have no
28
+ recorded source and stay until their entity is unreferenced.
29
+ - In encrypt and redact modes, a slot value that contains a detected PII span
30
+ is stored as a fingerprint, keyed with the PII encryption key when one is
31
+ configured. Slot values were taken from the original
32
+ utterance, so encrypt mode still wrote plaintext emails and phone numbers to
33
+ disk. Comparisons are unchanged. A value written before 0.4.7 compares as
34
+ different from the same value written now, so it is superseded once.
35
+ - `consolidate_pending` no longer drops a new value that reads the same once
36
+ redacted. Two emails saved with `add_fast` both stored as `[EMAIL]`, so the
37
+ second consolidated into a no-op and was lost.
38
+ - Recall adds a lexical ranking for identifiers (tokens of three or more
39
+ characters that contain a digit, such as order numbers and SKUs). An exact
40
+ identifier match passes the relevance floor. Stores gain `search_text`; no
41
+ schema change.
42
+ - `pii_detect_names_and_addresses=True` also redacts street addresses and cued
43
+ names ("my name is", "call me", "this is" followed by two capitalized
44
+ words). Off by default, because a redacted address is hidden from the agent
45
+ too.
46
+ - Appointment kinds get their own slots: "my dentist appointment" and "my
47
+ haircut appointment" no longer replace or merge with each other. Kind
48
+ entities such as `dentist_appointment` use the booking class, and a generic
49
+ `appointment` slot is still accepted where the text names a kind.
50
+ - The relevance floor no longer recommends a fixed 0.15 to 0.25 band. Cosine
51
+ ranges are embedder-specific, and a floor cannot tell whether a memory
52
+ answers a request.
53
+ - README and docs replace v0.1.0 performance numbers and fixed test counts
54
+ with current measurements, and state that no competitor arms ship in the
55
+ bake-off.
56
+
5
57
  ## 0.4.6 — 2026-10-08
6
58
 
7
59
  - A lower-trust write no longer replaces a first-party fact in its slot.
@@ -0,0 +1,71 @@
1
+ # Commercial use and trademark
2
+
3
+ The software in this repository is open source under the Apache License,
4
+ Version 2.0 (see [LICENSE](LICENSE)). That license covers the code. It
5
+ does not cover the name.
6
+
7
+ Releases through `v0.4.8`, and the code published through `c84d78b`, are
8
+ Apache-2.0 with no profile cap. Those grants are permanent, including
9
+ for forks already taken. There is no license key and no phone-home check.
10
+
11
+ ## Profile line from 1.0
12
+
13
+ `pycontextdb` 1.0 is a fresh release. It does not relicense 0.4.8 or any
14
+ earlier package. Production use of 1.0 and later requires a commercial
15
+ license from Atoms AI above 10,000 active profiles in a month. An active
16
+ profile is one person whose agent used ContextDB that month, counted once.
17
+ At or below that line, use stays free, including commercial use and
18
+ self-hosting. The free grant does not depend on a license key inside the
19
+ program, and it does not depend on revenue.
20
+
21
+ The 1.0 `LICENSE` file will carry this split, after legal review of the
22
+ text. Until that tag exists, Apache-2.0 is the grant for the code in this
23
+ repository.
24
+
25
+ New substantial contributions are accepted only under the CLA in
26
+ [CONTRIBUTING.md](CONTRIBUTING.md), so Atoms AI can license future work on
27
+ these terms. The CLA does not revoke Apache-2.0 on anything already released.
28
+
29
+ ## Trademark
30
+
31
+ **ContextDB** and the ContextDB logo are trademarks of Atoms AI. The
32
+ Apache License is a copyright and patent license; it does not grant
33
+ rights to the marks (Apache License §6).
34
+
35
+ You may:
36
+
37
+ - Say you use ContextDB, cite the project, and link to this repository.
38
+ - State compatibility ("works with ContextDB") when that is true.
39
+
40
+ You may not, without a commercial agreement with Atoms AI:
41
+
42
+ - Offer ContextDB as a hosted or managed service, or resell it as part
43
+ of a service, under the ContextDB name or branding.
44
+ - Use ContextDB or a confusingly similar mark as the name of a competing
45
+ product, hosted service, or company.
46
+ - Use the logo or word mark to imply endorsement by Atoms AI.
47
+ - Register domains, social handles, or trademarks that trade on the mark.
48
+
49
+ ## Resale and commercial agreements
50
+
51
+ For releases through `v0.4.8`, Apache-2.0 permits commercial use,
52
+ modification, redistribution, embedding, and hosted use of the code,
53
+ subject to its terms. No separate copyright license is required for those
54
+ uses.
55
+
56
+ If your company wants to use the ContextDB name or logo for a hosted offering,
57
+ or wants a supported OEM arrangement, Cloud service, support, or a trademark
58
+ agreement, contact **contextdb@atomsai.com**. Organizations above 10,000 active profiles in a month should use the
59
+ same address for the commercial license that applies to 1.0 and later.
60
+
61
+ ## Attribution (optional)
62
+
63
+ Apache-2.0 does not require a public credit line. If you ship a product
64
+ that uses ContextDB and want to credit the project, the preferred form
65
+ is a visible **Built with ContextDB** link to
66
+ https://github.com/atomsai/contextdb. That is a courtesy, not a license
67
+ condition. Redistributors must still keep `LICENSE` and `NOTICE` as
68
+ Apache requires.
69
+
70
+ Research citations should use the paper:
71
+ https://zenodo.org/records/19647089
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pycontextdb
3
- Version: 0.4.6
3
+ Version: 0.4.8
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,10 +74,10 @@ 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-195%20passing-brightgreen.svg" alt="Tests"></a>
77
+ <a href="https://github.com/atomsai/contextdb/actions/workflows/ci.yml"><img src="https://github.com/atomsai/contextdb/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></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
- <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>
80
+ <a href="https://github.com/atomsai/contextdb/blob/main/benchmarks/run_benchmarks.py"><img src="https://img.shields.io/badge/search_p95-18ms_%40_5K_(mock_embedder)-blue.svg" alt="Search p95 18 ms at 5K memories with the mock embedder"></a>
81
81
  <a href="https://github.com/atomsai/contextdb/stargazers"><img src="https://img.shields.io/github/stars/atomsai/contextdb?style=social" alt="GitHub stars"></a>
82
82
  </p>
83
83
 
@@ -91,20 +91,18 @@ Description-Content-Type: text/markdown
91
91
 
92
92
  ## At a glance
93
93
 
94
- <p align="center">
95
- <img src="https://raw.githubusercontent.com/atomsai/contextdb/main/assets/performance.png" alt="Performance at a glance — search p50 3.4ms, 1,930 writes/sec, 112K PII/sec, 5.0ms p95 at 5K memories, 800K vectors/sec index build, 82/82 tests" width="100%">
96
- </p>
97
-
98
94
  | | |
99
95
  |---|---|
100
- | **Write throughput** | 1,900+ memories/sec |
101
- | **Search latency (1K memories)** | p50 **3.4ms** · p95 **4.5ms** |
102
- | **Search latency (5K memories)** | p50 **3.9ms** · p95 **5.0ms** |
103
- | **Vector search (10K × 1,536d)** | p50 **0.8ms** · p95 **1.0ms** |
96
+ | **Write throughput** | about 1,600 to 1,800 memories/sec |
97
+ | **Search latency (1K memories)** | p50 **11 ms** · p95 **12 to 14 ms** |
98
+ | **Search latency (5K memories)** | p50 **17 ms** · p95 **18 ms** |
99
+ | **Vector search (10K × 1,536d)** | p50 **0.8 ms** · p95 **1.0 ms** |
104
100
  | **PII detection** | 100,000+ texts/sec |
105
- | **Tests** | 195 passing (incl. 34 trust-model acceptance evals) · ruff clean · mypy `--strict` clean |
101
+ | **Tests** | full suite and trust-model acceptance evals in CI on every pull request · ruff · mypy `--strict` |
106
102
  | **Dependencies** | SQLite + NumPy (FAISS / Postgres optional) |
107
103
 
104
+ Measured in October 2026 with `python benchmarks/run_benchmarks.py` on an Apple M4 Max, Python 3.12, SQLite, and the mock embedder, so no model call is included. These numbers are the SDK's own overhead; a real embedding model adds its own latency.
105
+
108
106
  Those search numbers are **SQLite + the in-repo mock embedder**. A recall
109
107
  that embeds the query with OpenAI is typically **100–300ms** before the
110
108
  store is touched. Use `embedding_cache_size`, `pip install 'pycontextdb[local]'`,
@@ -120,16 +118,16 @@ Speed is table stakes. The production failure that gets agents fired is **acting
120
118
 
121
119
  Every memory carries **epistemic provenance** (`user_stated` / `agent_inferred` / `third_party`), **confidence**, **independent corroboration** (same-speaker repeats do not count), and an **action-relevant** flag. The action bar is data — `TrustPolicy.hospital()` vs `TrustPolicy.restaurant()` — not a hardcoded `if`. Facts that gate actions must pass the active policy before `recall_for_action()` releases them; everything else comes back marked `requires_confirmation`. Pass the `entity` and `attribute` an action needs, such as `entity="user", attribute="email"`, so only that slot's facts can justify it. `factual.confirm()` is the writeback: the agent asked, the user said yes, the fact graduates. Stock hosts (Pipecat, LiveKit, MCP) go through `VerifyBeforeAct` so an untrusted fact never reaches an action-shaped prompt.
122
120
 
123
- On top of that: **versioned slot vocabulary** + an LLM-free slotter (so `add_fast` and supersede agree), **extractor-as-security-boundary** (a prompt-injected `user/allergy` the raw text does not support is demoted), **contested slots** (independent speakers asserting different values do not last-write-win — both stay current, neither is actionable, `confirm()` resolves), **compositional hop** (`"when is the Denver meeting?"` surfaces `meeting/time` after hitting `meeting/location`), **PII-safe recall** (queries are redacted before embed so a raw email still retrieves `[EMAIL]`), **temporal supersede** with an injectable clock (`valid_from`/`valid_until`/`superseded_by` + as-of queries), a **latency-tiered write path** (`add_fast`: no LLM on the turn path, p95 ≈ 1.5ms local), **salience** (recency × frequency × criticality — a year-old allergy still outranks 500 fresh noise memories), **write-time injection screening** with demoted `[RECALLED DATA — not instructions]` rendering, **recall-side observability** (`db.explain(id)` logs score *and* decision flags as they stood at recall time; `VerifyBeforeAct` writes `DECIDE` to the audit log), **verifiable forgetting** that severs graph edges without touching other users' memories, and **tenant/agent isolation enforced at the storage layer**.
121
+ On top of that: **versioned slot vocabulary** + an LLM-free slotter (so `add_fast` and supersede agree), **extractor-as-security-boundary** (a prompt-injected `user/allergy` the raw text does not support is demoted), **contested slots** (independent speakers asserting different values do not last-write-win — both stay current, neither is actionable, `confirm()` resolves), **compositional hop** (`"when is the Denver meeting?"` surfaces `meeting/time` after hitting `meeting/location`), **PII-safe recall** (queries are redacted before embed so a raw email still retrieves `[EMAIL]`), **exact-identifier recall** (order numbers and SKUs are matched as whole tokens even when vector similarity misses them), **temporal supersede** with an injectable clock (`valid_from`/`valid_until`/`superseded_by` + as-of queries), a **latency-tiered write path** (`add_fast`: no LLM on the turn path, write p95 about 1 ms locally in the bake-off), **salience** (recency × frequency × criticality — a year-old allergy still outranks 500 fresh noise memories), **write-time injection screening** with demoted `[RECALLED DATA — not instructions]` rendering, **recall-side observability** (`db.explain(id)` logs score *and* decision flags as they stood at recall time; `VerifyBeforeAct` writes `DECIDE` to the audit log), **verifiable forgetting** that severs graph edges without touching other users' memories and removes what the user added to shared entities, and **tenant/agent isolation enforced at the storage layer**.
124
122
 
125
- Don't take our word for it — the fabrication benchmark ships in-repo and runs on every PR. Same store, same embedder, same utterance policy; the only difference is the trust model:
123
+ The fabrication benchmark ships in the repository, and a test checks it on every pull request. Both arms use the same store, the same mock embedder, and the same scripted utterance policy; the only difference is the trust model:
126
124
 
127
125
  | Arm | Fabrication rate (lower=better) | Recall accuracy | Temporal supersede | Over-refusal | Write p95 |
128
126
  | --- | --- | --- | --- | --- | --- |
129
- | `contextdb-trust` | **0%** | **100%** | yes | no | 1.5ms |
130
- | `raw-store-baseline` (untyped control) | 50% | 88% | NO | no | 0.9ms |
127
+ | `contextdb-trust` | **0%** | **100%** | yes | no | 0.99 ms |
128
+ | `raw-store-baseline` (untyped control) | 50% | 88% | NO | no | 0.45 ms |
131
129
 
132
- Traps: wish-vs-fact, temporal move, prompt injection, PII redaction, cross-tenant bleed, negation, quantity update, over-refusal. Run it: `python benchmarks/trust_bakeoff.py` — acceptance evals live in `tests/evals/`. Mem0/Zep/Letta/supermemory arms implement the `MemoryArm` protocol and plug into the same harness.
130
+ Traps: wish-vs-fact, temporal move, prompt injection, PII redaction, cross-tenant bleed, negation, quantity update, over-refusal. Run it with `python benchmarks/trust_bakeoff.py`. These eight synthetic traps are regression evidence inside the harness, not a measure of production behavior, and no other memory product has been run through it. Another system can be compared by implementing the `MemoryArm` protocol. The acceptance evals live in `tests/evals/`.
133
131
 
134
132
  ---
135
133
 
@@ -507,49 +505,44 @@ Five layers, one dependency. Every component is modular, testable, and replaceab
507
505
 
508
506
  ## Benchmarks
509
507
 
510
- <p align="center">
511
- <img src="https://raw.githubusercontent.com/atomsai/contextdb/main/assets/benchmarks.png" alt="ContextDB v0.1.0 performance dashboard — search p50 3.4ms, 1,930 writes/sec, 112K PII/sec, 82/82 tests" width="100%">
512
- </p>
513
-
514
- Six workloads, all hermetic, all reproducible. No API keys, no network, no cached results — just `python benchmarks/run_benchmarks.py`.
508
+ Six workloads, all hermetic and reproducible: no API keys, no network, no cached results. Run `python benchmarks/run_benchmarks.py`. The numbers below are from October 2026 on an Apple M4 Max with Python 3.12, SQLite, the mock embedder, and no FAISS. They measure the SDK's own overhead; a real embedding model adds its own latency.
515
509
 
516
510
  ### 1. Write throughput
517
511
 
518
- 1,000 sequential `add()` calls including Pydantic validation, embedding, SQLite INSERT, and vector-index update.
512
+ 1,000 sequential `add()` calls including Pydantic validation, embedding, trust checks, SQLite INSERT, and vector-index update.
519
513
 
520
514
  ```
521
- Throughput: 1,930 writes/sec
522
- Average per write: 0.52ms
515
+ Throughput: about 1,600 to 1,800 writes/sec
516
+ Average per write: 0.6ms
523
517
  ```
524
518
 
525
- ### 2. Search latency — 100 queries against 1,000 memories
519
+ ### 2. Search latency: 100 queries against 1,000 memories
526
520
 
527
521
  ```
528
- p50: 3.40ms
529
- p95: 4.54ms
530
- p99: 4.75ms
531
- Mean: 3.59ms
522
+ p50: 11.0ms
523
+ p95: 14.3ms
524
+ p99: 21.1ms
525
+ Mean: 11.7ms
532
526
  ```
533
527
 
534
- Target was <100ms p95. We hit it by an order of magnitude with room to spare.
528
+ Search includes PII redaction of the query, vector and graph fusion, salience, the trust filter, and the read audit.
535
529
 
536
530
  ### 3. Search latency vs scale
537
531
 
538
- Latency grows sub-linearly; write cost stays flat until the vector index rebuilds.
539
-
540
532
  | Memories | Add (ms/op) | Search p50 | Search p95 |
541
533
  |---:|---:|---:|---:|
542
- | 100 | 0.47ms | 3.26ms | 4.25ms |
543
- | 500 | 0.45ms | 3.59ms | 4.16ms |
544
- | 1,000 | 0.43ms | 3.50ms | 4.24ms |
545
- | 5,000 | 0.53ms | 3.89ms | 4.96ms |
534
+ | 100 | 0.52ms | 9.2ms | 9.8ms |
535
+ | 500 | 0.55ms | 10.0ms | 11.6ms |
536
+ | 1,000 | 0.55ms | 11.1ms | 14.3ms |
537
+ | 5,000 | 0.55ms | 17.2ms | 17.8ms |
546
538
 
547
- ### 4. PII detection & redaction — 1,000 texts
539
+ ### 4. PII detection & redaction: 1,000 texts
548
540
 
549
541
  ```
550
- Throughput: 111,745 texts/sec
542
+ Throughput: 128,194 texts/sec
551
543
  Per text: 0.01ms
552
544
  Types covered: EMAIL, PHONE, SSN, CREDIT_CARD
545
+ (ADDRESS and cued NAME with pii_detect_names_and_addresses=True)
553
546
  ```
554
547
 
555
548
  Correctness spot-check:
@@ -559,29 +552,29 @@ Input: Contact me at test@example.com or 555-123-4567. SSN: 123-45-6789
559
552
  Redacted: Contact me at [EMAIL] or [PHONE]. SSN: [SSN]
560
553
  ```
561
554
 
562
- PII runs before content reaches storage. It never gets embedded. It never gets logged.
555
+ PII runs before content reaches storage. It never gets embedded. It never gets logged. In encrypt mode, slot values that contain PII are stored as keyed fingerprints, so no plaintext email or phone number is written to disk.
563
556
 
564
- ### 5. Vector index — 10K × 1,536-dim vectors
557
+ ### 5. Vector index: 10K × 1,536-dim vectors
565
558
 
566
559
  Pure-NumPy brute force, no FAISS dependency.
567
560
 
568
561
  ```
569
- Index build: 0.013s (~800K vectors/sec)
570
- Search p50: 0.80ms
571
- Search p95: 1.04ms
562
+ Index build: 0.011 to 0.017s
563
+ Search p50: 0.8ms
564
+ Search p95: 1.0ms
572
565
  Self-retrieval accuracy: PASS
573
566
  ```
574
567
 
575
568
  FAISS is available as an optional backend for datasets beyond ~100K vectors.
576
569
 
577
- ### 6. End-to-end — customer support agent
570
+ ### 6. End-to-end: customer support agent
578
571
 
579
- 50 mixed factual/experiential memories, 20 semantic searches, one PII-laden write with redaction verification.
572
+ 50 mixed factual and experiential memories, 20 semantic searches, one PII-laden write with redaction verification.
580
573
 
581
574
  ```
582
- Add 50 memories: 0.03s (1,849/sec)
583
- Search p50: 1.70ms
584
- PII add + redact: 0.76ms
575
+ Add 50 memories: 0.03s (about 1,540/sec)
576
+ Search p50: 4.9ms
577
+ PII add + redact: 1.4ms
585
578
  PII correctly redacted: PASS
586
579
  ```
587
580
 
@@ -592,7 +585,7 @@ Input: Customer John Smith, email john@acme.com, SSN 123-45-6789 called about
592
585
  Stored: Customer John Smith, email [EMAIL], SSN [SSN] called about billing
593
586
  ```
594
587
 
595
- Numbers above are from a MacBook-class laptop with no FAISS. Rerun `python benchmarks/run_benchmarks.py` on your own hardware — the script prints everything you see here.
588
+ Rerun `python benchmarks/run_benchmarks.py` on your own hardware; the script prints everything shown here.
596
589
 
597
590
  ---
598
591
 
@@ -8,10 +8,10 @@
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-195%20passing-brightgreen.svg" alt="Tests"></a>
11
+ <a href="https://github.com/atomsai/contextdb/actions/workflows/ci.yml"><img src="https://github.com/atomsai/contextdb/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></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
- <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>
14
+ <a href="https://github.com/atomsai/contextdb/blob/main/benchmarks/run_benchmarks.py"><img src="https://img.shields.io/badge/search_p95-18ms_%40_5K_(mock_embedder)-blue.svg" alt="Search p95 18 ms at 5K memories with the mock embedder"></a>
15
15
  <a href="https://github.com/atomsai/contextdb/stargazers"><img src="https://img.shields.io/github/stars/atomsai/contextdb?style=social" alt="GitHub stars"></a>
16
16
  </p>
17
17
 
@@ -25,20 +25,18 @@
25
25
 
26
26
  ## At a glance
27
27
 
28
- <p align="center">
29
- <img src="https://raw.githubusercontent.com/atomsai/contextdb/main/assets/performance.png" alt="Performance at a glance — search p50 3.4ms, 1,930 writes/sec, 112K PII/sec, 5.0ms p95 at 5K memories, 800K vectors/sec index build, 82/82 tests" width="100%">
30
- </p>
31
-
32
28
  | | |
33
29
  |---|---|
34
- | **Write throughput** | 1,900+ memories/sec |
35
- | **Search latency (1K memories)** | p50 **3.4ms** · p95 **4.5ms** |
36
- | **Search latency (5K memories)** | p50 **3.9ms** · p95 **5.0ms** |
37
- | **Vector search (10K × 1,536d)** | p50 **0.8ms** · p95 **1.0ms** |
30
+ | **Write throughput** | about 1,600 to 1,800 memories/sec |
31
+ | **Search latency (1K memories)** | p50 **11 ms** · p95 **12 to 14 ms** |
32
+ | **Search latency (5K memories)** | p50 **17 ms** · p95 **18 ms** |
33
+ | **Vector search (10K × 1,536d)** | p50 **0.8 ms** · p95 **1.0 ms** |
38
34
  | **PII detection** | 100,000+ texts/sec |
39
- | **Tests** | 195 passing (incl. 34 trust-model acceptance evals) · ruff clean · mypy `--strict` clean |
35
+ | **Tests** | full suite and trust-model acceptance evals in CI on every pull request · ruff · mypy `--strict` |
40
36
  | **Dependencies** | SQLite + NumPy (FAISS / Postgres optional) |
41
37
 
38
+ Measured in October 2026 with `python benchmarks/run_benchmarks.py` on an Apple M4 Max, Python 3.12, SQLite, and the mock embedder, so no model call is included. These numbers are the SDK's own overhead; a real embedding model adds its own latency.
39
+
42
40
  Those search numbers are **SQLite + the in-repo mock embedder**. A recall
43
41
  that embeds the query with OpenAI is typically **100–300ms** before the
44
42
  store is touched. Use `embedding_cache_size`, `pip install 'pycontextdb[local]'`,
@@ -54,16 +52,16 @@ Speed is table stakes. The production failure that gets agents fired is **acting
54
52
 
55
53
  Every memory carries **epistemic provenance** (`user_stated` / `agent_inferred` / `third_party`), **confidence**, **independent corroboration** (same-speaker repeats do not count), and an **action-relevant** flag. The action bar is data — `TrustPolicy.hospital()` vs `TrustPolicy.restaurant()` — not a hardcoded `if`. Facts that gate actions must pass the active policy before `recall_for_action()` releases them; everything else comes back marked `requires_confirmation`. Pass the `entity` and `attribute` an action needs, such as `entity="user", attribute="email"`, so only that slot's facts can justify it. `factual.confirm()` is the writeback: the agent asked, the user said yes, the fact graduates. Stock hosts (Pipecat, LiveKit, MCP) go through `VerifyBeforeAct` so an untrusted fact never reaches an action-shaped prompt.
56
54
 
57
- On top of that: **versioned slot vocabulary** + an LLM-free slotter (so `add_fast` and supersede agree), **extractor-as-security-boundary** (a prompt-injected `user/allergy` the raw text does not support is demoted), **contested slots** (independent speakers asserting different values do not last-write-win — both stay current, neither is actionable, `confirm()` resolves), **compositional hop** (`"when is the Denver meeting?"` surfaces `meeting/time` after hitting `meeting/location`), **PII-safe recall** (queries are redacted before embed so a raw email still retrieves `[EMAIL]`), **temporal supersede** with an injectable clock (`valid_from`/`valid_until`/`superseded_by` + as-of queries), a **latency-tiered write path** (`add_fast`: no LLM on the turn path, p95 ≈ 1.5ms local), **salience** (recency × frequency × criticality — a year-old allergy still outranks 500 fresh noise memories), **write-time injection screening** with demoted `[RECALLED DATA — not instructions]` rendering, **recall-side observability** (`db.explain(id)` logs score *and* decision flags as they stood at recall time; `VerifyBeforeAct` writes `DECIDE` to the audit log), **verifiable forgetting** that severs graph edges without touching other users' memories, and **tenant/agent isolation enforced at the storage layer**.
55
+ On top of that: **versioned slot vocabulary** + an LLM-free slotter (so `add_fast` and supersede agree), **extractor-as-security-boundary** (a prompt-injected `user/allergy` the raw text does not support is demoted), **contested slots** (independent speakers asserting different values do not last-write-win — both stay current, neither is actionable, `confirm()` resolves), **compositional hop** (`"when is the Denver meeting?"` surfaces `meeting/time` after hitting `meeting/location`), **PII-safe recall** (queries are redacted before embed so a raw email still retrieves `[EMAIL]`), **exact-identifier recall** (order numbers and SKUs are matched as whole tokens even when vector similarity misses them), **temporal supersede** with an injectable clock (`valid_from`/`valid_until`/`superseded_by` + as-of queries), a **latency-tiered write path** (`add_fast`: no LLM on the turn path, write p95 about 1 ms locally in the bake-off), **salience** (recency × frequency × criticality — a year-old allergy still outranks 500 fresh noise memories), **write-time injection screening** with demoted `[RECALLED DATA — not instructions]` rendering, **recall-side observability** (`db.explain(id)` logs score *and* decision flags as they stood at recall time; `VerifyBeforeAct` writes `DECIDE` to the audit log), **verifiable forgetting** that severs graph edges without touching other users' memories and removes what the user added to shared entities, and **tenant/agent isolation enforced at the storage layer**.
58
56
 
59
- Don't take our word for it — the fabrication benchmark ships in-repo and runs on every PR. Same store, same embedder, same utterance policy; the only difference is the trust model:
57
+ The fabrication benchmark ships in the repository, and a test checks it on every pull request. Both arms use the same store, the same mock embedder, and the same scripted utterance policy; the only difference is the trust model:
60
58
 
61
59
  | Arm | Fabrication rate (lower=better) | Recall accuracy | Temporal supersede | Over-refusal | Write p95 |
62
60
  | --- | --- | --- | --- | --- | --- |
63
- | `contextdb-trust` | **0%** | **100%** | yes | no | 1.5ms |
64
- | `raw-store-baseline` (untyped control) | 50% | 88% | NO | no | 0.9ms |
61
+ | `contextdb-trust` | **0%** | **100%** | yes | no | 0.99 ms |
62
+ | `raw-store-baseline` (untyped control) | 50% | 88% | NO | no | 0.45 ms |
65
63
 
66
- Traps: wish-vs-fact, temporal move, prompt injection, PII redaction, cross-tenant bleed, negation, quantity update, over-refusal. Run it: `python benchmarks/trust_bakeoff.py` — acceptance evals live in `tests/evals/`. Mem0/Zep/Letta/supermemory arms implement the `MemoryArm` protocol and plug into the same harness.
64
+ Traps: wish-vs-fact, temporal move, prompt injection, PII redaction, cross-tenant bleed, negation, quantity update, over-refusal. Run it with `python benchmarks/trust_bakeoff.py`. These eight synthetic traps are regression evidence inside the harness, not a measure of production behavior, and no other memory product has been run through it. Another system can be compared by implementing the `MemoryArm` protocol. The acceptance evals live in `tests/evals/`.
67
65
 
68
66
  ---
69
67
 
@@ -441,49 +439,44 @@ Five layers, one dependency. Every component is modular, testable, and replaceab
441
439
 
442
440
  ## Benchmarks
443
441
 
444
- <p align="center">
445
- <img src="https://raw.githubusercontent.com/atomsai/contextdb/main/assets/benchmarks.png" alt="ContextDB v0.1.0 performance dashboard — search p50 3.4ms, 1,930 writes/sec, 112K PII/sec, 82/82 tests" width="100%">
446
- </p>
447
-
448
- Six workloads, all hermetic, all reproducible. No API keys, no network, no cached results — just `python benchmarks/run_benchmarks.py`.
442
+ Six workloads, all hermetic and reproducible: no API keys, no network, no cached results. Run `python benchmarks/run_benchmarks.py`. The numbers below are from October 2026 on an Apple M4 Max with Python 3.12, SQLite, the mock embedder, and no FAISS. They measure the SDK's own overhead; a real embedding model adds its own latency.
449
443
 
450
444
  ### 1. Write throughput
451
445
 
452
- 1,000 sequential `add()` calls including Pydantic validation, embedding, SQLite INSERT, and vector-index update.
446
+ 1,000 sequential `add()` calls including Pydantic validation, embedding, trust checks, SQLite INSERT, and vector-index update.
453
447
 
454
448
  ```
455
- Throughput: 1,930 writes/sec
456
- Average per write: 0.52ms
449
+ Throughput: about 1,600 to 1,800 writes/sec
450
+ Average per write: 0.6ms
457
451
  ```
458
452
 
459
- ### 2. Search latency — 100 queries against 1,000 memories
453
+ ### 2. Search latency: 100 queries against 1,000 memories
460
454
 
461
455
  ```
462
- p50: 3.40ms
463
- p95: 4.54ms
464
- p99: 4.75ms
465
- Mean: 3.59ms
456
+ p50: 11.0ms
457
+ p95: 14.3ms
458
+ p99: 21.1ms
459
+ Mean: 11.7ms
466
460
  ```
467
461
 
468
- Target was <100ms p95. We hit it by an order of magnitude with room to spare.
462
+ Search includes PII redaction of the query, vector and graph fusion, salience, the trust filter, and the read audit.
469
463
 
470
464
  ### 3. Search latency vs scale
471
465
 
472
- Latency grows sub-linearly; write cost stays flat until the vector index rebuilds.
473
-
474
466
  | Memories | Add (ms/op) | Search p50 | Search p95 |
475
467
  |---:|---:|---:|---:|
476
- | 100 | 0.47ms | 3.26ms | 4.25ms |
477
- | 500 | 0.45ms | 3.59ms | 4.16ms |
478
- | 1,000 | 0.43ms | 3.50ms | 4.24ms |
479
- | 5,000 | 0.53ms | 3.89ms | 4.96ms |
468
+ | 100 | 0.52ms | 9.2ms | 9.8ms |
469
+ | 500 | 0.55ms | 10.0ms | 11.6ms |
470
+ | 1,000 | 0.55ms | 11.1ms | 14.3ms |
471
+ | 5,000 | 0.55ms | 17.2ms | 17.8ms |
480
472
 
481
- ### 4. PII detection & redaction — 1,000 texts
473
+ ### 4. PII detection & redaction: 1,000 texts
482
474
 
483
475
  ```
484
- Throughput: 111,745 texts/sec
476
+ Throughput: 128,194 texts/sec
485
477
  Per text: 0.01ms
486
478
  Types covered: EMAIL, PHONE, SSN, CREDIT_CARD
479
+ (ADDRESS and cued NAME with pii_detect_names_and_addresses=True)
487
480
  ```
488
481
 
489
482
  Correctness spot-check:
@@ -493,29 +486,29 @@ Input: Contact me at test@example.com or 555-123-4567. SSN: 123-45-6789
493
486
  Redacted: Contact me at [EMAIL] or [PHONE]. SSN: [SSN]
494
487
  ```
495
488
 
496
- PII runs before content reaches storage. It never gets embedded. It never gets logged.
489
+ PII runs before content reaches storage. It never gets embedded. It never gets logged. In encrypt mode, slot values that contain PII are stored as keyed fingerprints, so no plaintext email or phone number is written to disk.
497
490
 
498
- ### 5. Vector index — 10K × 1,536-dim vectors
491
+ ### 5. Vector index: 10K × 1,536-dim vectors
499
492
 
500
493
  Pure-NumPy brute force, no FAISS dependency.
501
494
 
502
495
  ```
503
- Index build: 0.013s (~800K vectors/sec)
504
- Search p50: 0.80ms
505
- Search p95: 1.04ms
496
+ Index build: 0.011 to 0.017s
497
+ Search p50: 0.8ms
498
+ Search p95: 1.0ms
506
499
  Self-retrieval accuracy: PASS
507
500
  ```
508
501
 
509
502
  FAISS is available as an optional backend for datasets beyond ~100K vectors.
510
503
 
511
- ### 6. End-to-end — customer support agent
504
+ ### 6. End-to-end: customer support agent
512
505
 
513
- 50 mixed factual/experiential memories, 20 semantic searches, one PII-laden write with redaction verification.
506
+ 50 mixed factual and experiential memories, 20 semantic searches, one PII-laden write with redaction verification.
514
507
 
515
508
  ```
516
- Add 50 memories: 0.03s (1,849/sec)
517
- Search p50: 1.70ms
518
- PII add + redact: 0.76ms
509
+ Add 50 memories: 0.03s (about 1,540/sec)
510
+ Search p50: 4.9ms
511
+ PII add + redact: 1.4ms
519
512
  PII correctly redacted: PASS
520
513
  ```
521
514
 
@@ -526,7 +519,7 @@ Input: Customer John Smith, email john@acme.com, SSN 123-45-6789 called about
526
519
  Stored: Customer John Smith, email [EMAIL], SSN [SSN] called about billing
527
520
  ```
528
521
 
529
- Numbers above are from a MacBook-class laptop with no FAISS. Rerun `python benchmarks/run_benchmarks.py` on your own hardware — the script prints everything you see here.
522
+ Rerun `python benchmarks/run_benchmarks.py` on your own hardware; the script prints everything shown here.
530
523
 
531
524
  ---
532
525
 
@@ -41,7 +41,7 @@ from contextdb.core.policy import TrustPolicy
41
41
  from contextdb.pool import ContextDBPool
42
42
  from contextdb.utils.embeddings import EmbeddingProvider
43
43
 
44
- __version__ = "0.4.6"
44
+ __version__ = "0.4.8"
45
45
 
46
46
  __all__ = [
47
47
  "Clock",