pycontextdb 0.4.5__tar.gz → 0.4.7__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.5 → pycontextdb-0.4.7}/CHANGELOG.md +56 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/PKG-INFO +44 -51
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/README.md +43 -50
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/__init__.py +1 -1
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/client.py +106 -7
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/core/config.py +13 -3
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/core/policy.py +6 -4
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/core/slots.py +68 -3
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/dynamics/retrieval.py +50 -1
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/dynamics/trust.py +85 -31
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/graphs/entity.py +118 -12
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/memory/factual.py +20 -5
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/privacy/pii_detector.py +40 -5
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/store/base.py +19 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/store/postgres_store.py +21 -1
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/store/sqlite_store.py +23 -1
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/index.md +8 -4
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/prompts.md +1 -1
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/quickstart.md +3 -1
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/pyproject.toml +1 -1
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/evals/test_trust_model.py +398 -1
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_pii_detector.py +33 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_retrieval.py +12 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/.github/CODEOWNERS +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/.github/copilot-instructions.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/.github/pull_request_template.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/.github/workflows/ci.yml +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/.github/workflows/publish.yml +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/.gitignore +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/.pre-commit-config.yaml +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/COMMERCIAL.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/CONTRIBUTING.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/LICENSE +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/NOTICE +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/OPEN_CORE.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/ai-tools/.cursorrules +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/ai-tools/CLAUDE.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/ai-tools/README.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/ai-tools/llms-full.txt +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/ai-tools/llms.txt +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/assets/README.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/assets/architecture.png +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/assets/benchmarks.png +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/assets/hero.png +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/assets/performance.png +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/assets/problem.png +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/assets/quickstart.png +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/benchmarks/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/benchmarks/run_benchmarks.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/benchmarks/trust_bakeoff.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/agents/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/agents/memory_bus.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/agents/rl_manager.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/cli.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/connectors/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/connectors/base.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/connectors/postgres.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/core/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/core/clock.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/core/exceptions.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/core/models.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/dynamics/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/dynamics/evolution.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/dynamics/formation.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/dynamics/salience.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/graphs/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/graphs/base.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/graphs/causal.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/graphs/semantic.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/graphs/temporal.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/act.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/autogen.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/crewai.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/langchain.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/livekit.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/openai_tools.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/pipecat.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/integrations/prompting.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/mcp.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/memory/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/memory/experiential.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/memory/working.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/pool.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/privacy/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/privacy/audit.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/privacy/injection.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/privacy/retention.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/py.typed +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/serve.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/store/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/store/factory.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/store/pg_sql.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/store/vector_index.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/utils/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/utils/embeddings.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/utils/llm.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/contextdb/utils/migrations.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/api.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/architecture.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/connectors.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/memory_evolution.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/migrations.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/multi_tenant.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/postgres.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/serve.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/docs/trust_policy.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/examples/customer_support.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/examples/multi_agent_team.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/examples/phone_agent.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/examples/research_assistant.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/legal/CLA-entity.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/legal/CLA-individual.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/legal/signed/README.md +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/mkdocs.yml +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/scripts/check_open_core_boundary.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/scripts/check_release.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/conftest.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/evals/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/integration/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/integration/test_client.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/integration/test_langchain_integration.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/pg_util.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/test_version.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/__init__.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_audit.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_config.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_connectors.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_decision_scope.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_embeddings.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_evolution.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_formation.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_graphs.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_host_api.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_init.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_memory_bus.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_memory_evolution.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_models.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_open_core_boundary.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_pg_sql.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_postgres_concurrency.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_postgres_connector.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_postgres_memory_evolution.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_retention.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_scoped_id_access.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_serve.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_store.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_trust_policy.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_vector_index.py +0 -0
- {pycontextdb-0.4.5 → pycontextdb-0.4.7}/tests/unit/test_vector_scope.py +0 -0
|
@@ -2,6 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.4.7 — 2026-10-12
|
|
6
|
+
|
|
7
|
+
- `forget_user` now removes what the user added to shared entities. Entity
|
|
8
|
+
attributes record the memory that supplied them, are rebuilt from the
|
|
9
|
+
remaining sources when a memory is removed, and an entity no memory still
|
|
10
|
+
mentions is deleted. `verify_forgotten` fails if an edge or attribute source
|
|
11
|
+
still points at a deleted memory. Attributes merged before 0.4.7 have no
|
|
12
|
+
recorded source and stay until their entity is unreferenced.
|
|
13
|
+
- In encrypt and redact modes, a slot value that contains a detected PII span
|
|
14
|
+
is stored as a fingerprint, keyed with the PII encryption key when one is
|
|
15
|
+
configured. Slot values were taken from the original
|
|
16
|
+
utterance, so encrypt mode still wrote plaintext emails and phone numbers to
|
|
17
|
+
disk. Comparisons are unchanged. A value written before 0.4.7 compares as
|
|
18
|
+
different from the same value written now, so it is superseded once.
|
|
19
|
+
- `consolidate_pending` no longer drops a new value that reads the same once
|
|
20
|
+
redacted. Two emails saved with `add_fast` both stored as `[EMAIL]`, so the
|
|
21
|
+
second consolidated into a no-op and was lost.
|
|
22
|
+
- Recall adds a lexical ranking for identifiers (tokens of three or more
|
|
23
|
+
characters that contain a digit, such as order numbers and SKUs). An exact
|
|
24
|
+
identifier match passes the relevance floor. Stores gain `search_text`; no
|
|
25
|
+
schema change.
|
|
26
|
+
- `pii_detect_names_and_addresses=True` also redacts street addresses and cued
|
|
27
|
+
names ("my name is", "call me", "this is" followed by two capitalized
|
|
28
|
+
words). Off by default, because a redacted address is hidden from the agent
|
|
29
|
+
too.
|
|
30
|
+
- Appointment kinds get their own slots: "my dentist appointment" and "my
|
|
31
|
+
haircut appointment" no longer replace or merge with each other. Kind
|
|
32
|
+
entities such as `dentist_appointment` use the booking class, and a generic
|
|
33
|
+
`appointment` slot is still accepted where the text names a kind.
|
|
34
|
+
- The relevance floor no longer recommends a fixed 0.15 to 0.25 band. Cosine
|
|
35
|
+
ranges are embedder-specific, and a floor cannot tell whether a memory
|
|
36
|
+
answers a request.
|
|
37
|
+
- README and docs replace v0.1.0 performance numbers and fixed test counts
|
|
38
|
+
with current measurements, and state that no competitor arms ship in the
|
|
39
|
+
bake-off.
|
|
40
|
+
|
|
41
|
+
## 0.4.6 — 2026-10-08
|
|
42
|
+
|
|
43
|
+
- A lower-trust write no longer replaces a first-party fact in its slot.
|
|
44
|
+
Writes made under one user id share a speaker, so a third-party document or
|
|
45
|
+
an agent inference stored for that user superseded what the user had said,
|
|
46
|
+
and the user would then be asked to confirm the document's value. A
|
|
47
|
+
user-stated or confirmed fact now stays current and the slot is contested
|
|
48
|
+
until `confirm()`. Corrections of equal or higher trust still supersede.
|
|
49
|
+
Advisory: GHSA-vpf9-5xgr-3x8m.
|
|
50
|
+
- `factual.recall_for_action` and `factual.pending_confirmations` accept the
|
|
51
|
+
`entity` and `attribute` an action needs. With them, only current facts in
|
|
52
|
+
that slot count, read exactly rather than by similarity, and the read is
|
|
53
|
+
audited like a recall. Without them nothing changes: any trusted memory the
|
|
54
|
+
query retrieves counts, whether or not it answers the request.
|
|
55
|
+
- The slot vocabulary (version 2) adds `user/billing_email`, `user/work_phone`,
|
|
56
|
+
and `user/work_address`. A user's billing email, work phone, or work address
|
|
57
|
+
no longer replaces their personal one. An email filed as a billing email
|
|
58
|
+
stays there instead of being moved to `user/email` and stored as
|
|
59
|
+
`agent_inferred`.
|
|
60
|
+
|
|
5
61
|
## 0.4.5 — 2026-10-05
|
|
6
62
|
|
|
7
63
|
- `forget_user` no longer deletes other users' memories. It added every graph
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pycontextdb
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.7
|
|
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]'`,
|
|
@@ -118,18 +116,18 @@ Hermetic, reproducible — run the suite yourself: `python benchmarks/run_benchm
|
|
|
118
116
|
|
|
119
117
|
Speed is table stakes. The production failure that gets agents fired is **acting on a wish as if it were a fact**: a caller says *"I'd like to come in Thursday"* and the agent answers *"we penciled you in."* A wish and a confirmed fact are the same object in every memory store on the market — so ContextDB types them.
|
|
120
118
|
|
|
121
|
-
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`. `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.
|
|
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]'`,
|
|
@@ -52,18 +50,18 @@ Hermetic, reproducible — run the suite yourself: `python benchmarks/run_benchm
|
|
|
52
50
|
|
|
53
51
|
Speed is table stakes. The production failure that gets agents fired is **acting on a wish as if it were a fact**: a caller says *"I'd like to come in Thursday"* and the agent answers *"we penciled you in."* A wish and a confirmed fact are the same object in every memory store on the market — so ContextDB types them.
|
|
54
52
|
|
|
55
|
-
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`. `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.
|
|
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
|
|