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.
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/CHANGELOG.md +52 -0
- pycontextdb-0.4.8/COMMERCIAL.md +71 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/PKG-INFO +43 -50
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/README.md +42 -49
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/__init__.py +1 -1
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/client.py +85 -8
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/config.py +13 -3
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/policy.py +6 -4
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/slots.py +49 -3
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/retrieval.py +187 -4
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/trust.py +12 -4
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/entity.py +118 -12
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/prompting.py +30 -7
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/memory/factual.py +4 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/pii_detector.py +40 -5
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/base.py +19 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/postgres_store.py +61 -6
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/sqlite_store.py +52 -8
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/index.md +8 -4
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/prompts.md +1 -1
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/quickstart.md +3 -1
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/pyproject.toml +1 -1
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/evals/test_trust_model.py +193 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_pii_detector.py +33 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_retrieval.py +73 -0
- pycontextdb-0.4.6/COMMERCIAL.md +0 -44
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/CODEOWNERS +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/copilot-instructions.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/pull_request_template.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/workflows/ci.yml +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.github/workflows/publish.yml +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.gitignore +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/.pre-commit-config.yaml +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/CONTRIBUTING.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/LICENSE +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/NOTICE +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/OPEN_CORE.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/.cursorrules +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/CLAUDE.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/README.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/llms-full.txt +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/ai-tools/llms.txt +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/README.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/architecture.png +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/benchmarks.png +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/hero.png +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/performance.png +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/problem.png +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/assets/quickstart.png +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/benchmarks/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/benchmarks/run_benchmarks.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/benchmarks/trust_bakeoff.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/agents/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/agents/memory_bus.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/agents/rl_manager.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/cli.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/connectors/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/connectors/base.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/connectors/postgres.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/clock.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/exceptions.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/core/models.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/evolution.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/formation.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/dynamics/salience.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/base.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/causal.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/semantic.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/graphs/temporal.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/act.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/autogen.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/crewai.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/langchain.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/livekit.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/openai_tools.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/integrations/pipecat.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/mcp.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/memory/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/memory/experiential.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/memory/working.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/pool.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/audit.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/injection.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/privacy/retention.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/py.typed +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/serve.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/factory.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/pg_sql.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/store/vector_index.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/utils/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/utils/embeddings.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/utils/llm.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/contextdb/utils/migrations.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/api.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/architecture.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/connectors.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/memory_evolution.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/migrations.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/multi_tenant.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/postgres.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/serve.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/docs/trust_policy.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/examples/customer_support.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/examples/multi_agent_team.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/examples/phone_agent.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/examples/research_assistant.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/legal/CLA-entity.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/legal/CLA-individual.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/legal/signed/README.md +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/mkdocs.yml +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/scripts/check_open_core_boundary.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/scripts/check_release.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/conftest.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/evals/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/integration/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/integration/test_client.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/integration/test_langchain_integration.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/pg_util.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/test_version.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/__init__.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_audit.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_config.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_connectors.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_decision_scope.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_embeddings.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_evolution.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_formation.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_graphs.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_host_api.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_init.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_memory_bus.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_memory_evolution.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_models.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_open_core_boundary.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_pg_sql.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_postgres_concurrency.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_postgres_connector.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_postgres_memory_evolution.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_retention.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_scoped_id_access.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_serve.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_store.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_trust_policy.py +0 -0
- {pycontextdb-0.4.6 → pycontextdb-0.4.8}/tests/unit/test_vector_index.py +0 -0
- {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.
|
|
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/
|
|
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
|
|
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,
|
|
101
|
-
| **Search latency (1K memories)** | p50 **
|
|
102
|
-
| **Search latency (5K memories)** | p50 **
|
|
103
|
-
| **Vector search (10K × 1,536d)** | p50 **0.
|
|
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** |
|
|
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
|
|
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
|
-
|
|
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 |
|
|
130
|
-
| `raw-store-baseline` (untyped control) | 50% | 88% | NO | no | 0.
|
|
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
|
|
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
|
-
|
|
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,
|
|
522
|
-
Average per write: 0.
|
|
515
|
+
Throughput: about 1,600 to 1,800 writes/sec
|
|
516
|
+
Average per write: 0.6ms
|
|
523
517
|
```
|
|
524
518
|
|
|
525
|
-
### 2. Search latency
|
|
519
|
+
### 2. Search latency: 100 queries against 1,000 memories
|
|
526
520
|
|
|
527
521
|
```
|
|
528
|
-
p50:
|
|
529
|
-
p95:
|
|
530
|
-
p99:
|
|
531
|
-
Mean:
|
|
522
|
+
p50: 11.0ms
|
|
523
|
+
p95: 14.3ms
|
|
524
|
+
p99: 21.1ms
|
|
525
|
+
Mean: 11.7ms
|
|
532
526
|
```
|
|
533
527
|
|
|
534
|
-
|
|
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.
|
|
543
|
-
| 500 | 0.
|
|
544
|
-
| 1,000 | 0.
|
|
545
|
-
| 5,000 | 0.
|
|
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
|
|
539
|
+
### 4. PII detection & redaction: 1,000 texts
|
|
548
540
|
|
|
549
541
|
```
|
|
550
|
-
Throughput:
|
|
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
|
|
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.
|
|
570
|
-
Search p50: 0.
|
|
571
|
-
Search p95: 1.
|
|
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
|
|
570
|
+
### 6. End-to-end: customer support agent
|
|
578
571
|
|
|
579
|
-
50 mixed factual
|
|
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,
|
|
583
|
-
Search p50:
|
|
584
|
-
PII add + redact:
|
|
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
|
-
|
|
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/
|
|
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
|
|
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,
|
|
35
|
-
| **Search latency (1K memories)** | p50 **
|
|
36
|
-
| **Search latency (5K memories)** | p50 **
|
|
37
|
-
| **Vector search (10K × 1,536d)** | p50 **0.
|
|
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** |
|
|
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
|
|
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
|
-
|
|
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 |
|
|
64
|
-
| `raw-store-baseline` (untyped control) | 50% | 88% | NO | no | 0.
|
|
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
|
|
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
|
-
|
|
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,
|
|
456
|
-
Average per write: 0.
|
|
449
|
+
Throughput: about 1,600 to 1,800 writes/sec
|
|
450
|
+
Average per write: 0.6ms
|
|
457
451
|
```
|
|
458
452
|
|
|
459
|
-
### 2. Search latency
|
|
453
|
+
### 2. Search latency: 100 queries against 1,000 memories
|
|
460
454
|
|
|
461
455
|
```
|
|
462
|
-
p50:
|
|
463
|
-
p95:
|
|
464
|
-
p99:
|
|
465
|
-
Mean:
|
|
456
|
+
p50: 11.0ms
|
|
457
|
+
p95: 14.3ms
|
|
458
|
+
p99: 21.1ms
|
|
459
|
+
Mean: 11.7ms
|
|
466
460
|
```
|
|
467
461
|
|
|
468
|
-
|
|
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.
|
|
477
|
-
| 500 | 0.
|
|
478
|
-
| 1,000 | 0.
|
|
479
|
-
| 5,000 | 0.
|
|
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
|
|
473
|
+
### 4. PII detection & redaction: 1,000 texts
|
|
482
474
|
|
|
483
475
|
```
|
|
484
|
-
Throughput:
|
|
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
|
|
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.
|
|
504
|
-
Search p50: 0.
|
|
505
|
-
Search p95: 1.
|
|
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
|
|
504
|
+
### 6. End-to-end: customer support agent
|
|
512
505
|
|
|
513
|
-
50 mixed factual
|
|
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,
|
|
517
|
-
Search p50:
|
|
518
|
-
PII add + redact:
|
|
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
|
-
|
|
522
|
+
Rerun `python benchmarks/run_benchmarks.py` on your own hardware; the script prints everything shown here.
|
|
530
523
|
|
|
531
524
|
---
|
|
532
525
|
|