connordb 1.8.1__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 (33) hide show
  1. connordb-1.8.1/.gitignore +85 -0
  2. connordb-1.8.1/PKG-INFO +228 -0
  3. connordb-1.8.1/README.md +199 -0
  4. connordb-1.8.1/pyproject.toml +53 -0
  5. connordb-1.8.1/src/connordb/__init__.py +125 -0
  6. connordb-1.8.1/src/connordb/_pb/__init__.py +0 -0
  7. connordb-1.8.1/src/connordb/_pb/connordb/__init__.py +0 -0
  8. connordb-1.8.1/src/connordb/_pb/connordb/v1/__init__.py +0 -0
  9. connordb-1.8.1/src/connordb/_pb/connordb/v1/cluster_pb2.py +96 -0
  10. connordb-1.8.1/src/connordb/_pb/connordb/v1/cluster_pb2_grpc.py +842 -0
  11. connordb-1.8.1/src/connordb/_pb/connordb/v1/collections_pb2.py +81 -0
  12. connordb-1.8.1/src/connordb/_pb/connordb/v1/collections_pb2_grpc.py +24 -0
  13. connordb-1.8.1/src/connordb/_pb/connordb/v1/common_pb2.py +38 -0
  14. connordb-1.8.1/src/connordb/_pb/connordb/v1/common_pb2_grpc.py +24 -0
  15. connordb-1.8.1/src/connordb/_pb/connordb/v1/embed_pb2.py +46 -0
  16. connordb-1.8.1/src/connordb/_pb/connordb/v1/embed_pb2_grpc.py +24 -0
  17. connordb-1.8.1/src/connordb/_pb/connordb/v1/engine_pb2.py +281 -0
  18. connordb-1.8.1/src/connordb/_pb/connordb/v1/engine_pb2_grpc.py +3056 -0
  19. connordb-1.8.1/src/connordb/_pb/connordb/v1/grammar_pb2.py +40 -0
  20. connordb-1.8.1/src/connordb/_pb/connordb/v1/grammar_pb2_grpc.py +100 -0
  21. connordb-1.8.1/src/connordb/_pb/connordb/v1/replication_pb2.py +51 -0
  22. connordb-1.8.1/src/connordb/_pb/connordb/v1/replication_pb2_grpc.py +157 -0
  23. connordb-1.8.1/src/connordb/_pb/connordb/v1/snapshots_pb2.py +46 -0
  24. connordb-1.8.1/src/connordb/_pb/connordb/v1/snapshots_pb2_grpc.py +24 -0
  25. connordb-1.8.1/src/connordb/_pb/connordb/v1/types_pb2.py +60 -0
  26. connordb-1.8.1/src/connordb/_pb/connordb/v1/types_pb2_grpc.py +24 -0
  27. connordb-1.8.1/src/connordb/errors.py +255 -0
  28. connordb-1.8.1/src/connordb/grpc.py +2775 -0
  29. connordb-1.8.1/src/connordb/retry.py +86 -0
  30. connordb-1.8.1/tests/conftest.py +13 -0
  31. connordb-1.8.1/tests/test_errors.py +211 -0
  32. connordb-1.8.1/tests/test_grpc_client.py +353 -0
  33. connordb-1.8.1/tests/test_request_shapes.py +1350 -0
@@ -0,0 +1,85 @@
1
+ # Rust
2
+ /target
3
+ **/*.rs.bk
4
+ Cargo.lock.bak
5
+
6
+ # IDE
7
+ .idea/
8
+ .vscode/
9
+ *.swp
10
+ *.swo
11
+ *~
12
+
13
+ # macOS
14
+ .DS_Store
15
+ ._*
16
+
17
+ # Windows
18
+ Thumbs.db
19
+
20
+ # Datasets (downloaded on demand by scripts/download-*.sh)
21
+ datasets/
22
+
23
+ # Benchmark results
24
+ benches/results/
25
+ criterion/
26
+
27
+ # Logs
28
+ *.log
29
+
30
+ # Environment
31
+ .env
32
+ .env.*
33
+ !.env.example
34
+ *.local
35
+
36
+ # Python stress-test venv
37
+ scripts/stress/.venv/
38
+
39
+ # Python bytecode caches
40
+ __pycache__/
41
+ *.py[cod]
42
+ *$py.class
43
+
44
+ # Python virtual environments + build artifacts
45
+ .venv/
46
+ venv/
47
+ *.egg-info/
48
+ *.egg
49
+ build/
50
+ dist/
51
+ .pytest_cache/
52
+ .ruff_cache/
53
+ .mypy_cache/
54
+ .tox/
55
+ .coverage
56
+ .coverage.*
57
+ htmlcov/
58
+
59
+ # Node (mkdocs deploy and similar)
60
+ node_modules/
61
+
62
+ # Helm chart packaging artifacts
63
+ *.tgz
64
+
65
+ # MkDocs build output
66
+ site/
67
+ m11-replay-results/
68
+
69
+ # Models
70
+ models/
71
+ *.onnx
72
+ *.safetensors
73
+
74
+ # Temporary
75
+ tmp/
76
+ temp/
77
+
78
+ # Claude Code transient state
79
+ .claude/scheduled_tasks.lock
80
+ .claude/projects/
81
+
82
+ # Ansible (the example template is committed; the real inventory
83
+ # carries IPs / SSH key paths and stays out of git).
84
+ deploy/ansible/inventory.yml
85
+ deploy/ansible/*.retry
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: connordb
3
+ Version: 1.8.1
4
+ Summary: Python SDK for the connordb engine (machine-only, gRPC). Atomic multi-modal commits, async embedding freshness, hybrid search.
5
+ Project-URL: Homepage, https://github.com/Identy-cloud/connordb
6
+ Project-URL: Documentation, https://github.com/Identy-cloud/connordb/tree/main/clients/connordb-py
7
+ Project-URL: Issues, https://github.com/Identy-cloud/connordb/issues
8
+ Author: connordb contributors
9
+ License: Proprietary
10
+ Keywords: ai,database,embedding,search,vector
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: Other/Proprietary License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Database
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: grpcio>=1.62
21
+ Requires-Dist: protobuf>=4.25
22
+ Requires-Dist: typing-extensions>=4.8; python_version < '3.11'
23
+ Provides-Extra: dev
24
+ Requires-Dist: grpcio-tools>=1.62; extra == 'dev'
25
+ Requires-Dist: pytest-cov>=4.1; extra == 'dev'
26
+ Requires-Dist: pytest>=7.4; extra == 'dev'
27
+ Requires-Dist: ruff>=0.1; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # connordb — Python SDK
31
+
32
+ Python SDK for [ConnorDB](https://github.com/Identy-cloud/connordb) — the system of record for agent memory. One ACID engine that gives a swarm of agents durable, auditable, tamper-evident memory: atomic multi-modal commit (row + BM25 + dense vector + sparse + typed payload under one fsync), in-process embed worker, LSN-watermark read-your-writes, server-side hybrid search. It also collapses the 5-service RAG stack (vector DB + document DB + embed service + queue + read-your-writes cache + retry + reranker) into that single engine. The SDK talks to a running engine over **gRPC** (the canonical machine transport, ADR 0006); the previous HTTP client was removed in v1.0.0.
33
+
34
+ A single `GrpcClient.insert(...)` call commits row + BM25 + dense vector + sparse + typed payload under one fsync; the embed worker runs server-side off the commit path; `wait_for_embed(lsn)` blocks on the LSN watermark; `search_hybrid(...)` fuses the indexes in one round-trip.
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ pip install connordb # full SDK (grpcio + protobuf)
40
+ ```
41
+
42
+ Or in dev mode:
43
+
44
+ ```bash
45
+ cd clients/connordb-py
46
+ pip install -e ".[dev]"
47
+ pytest
48
+ ```
49
+
50
+ ## Quick start
51
+
52
+ ```python
53
+ from connordb import GrpcClient
54
+
55
+ with GrpcClient(target="localhost:50051", api_key="…") as c:
56
+ # Atomic multi-modal commit.
57
+ lsn = c.insert(
58
+ id="doc-1",
59
+ text="ship the patch",
60
+ embedding=[0.1, 0.2, 0.3, 0.4], # match the engine's --embedding-dim
61
+ payload={"category": "urgent"},
62
+ collection="notes",
63
+ )
64
+
65
+ # Read-your-writes on the vector index.
66
+ c.wait_for_embed(lsn)
67
+
68
+ # Hybrid search: BM25 + dense fused via RRF.
69
+ hits = c.search_hybrid(query="patch", k=10, include_text=True)
70
+ for h in hits:
71
+ print(h["rank"], h["id"], h.get("distance") or h.get("score"))
72
+ ```
73
+
74
+ ## Surface
75
+
76
+ The full method list lives in [`src/connordb/grpc.py`](src/connordb/grpc.py).
77
+ The canonical wire contract is the proto under
78
+ [`proto/connordb/v1/`](https://github.com/Identy-cloud/connordb/tree/main/proto/connordb/v1)
79
+ and the QueryPlan AST in
80
+ [`docs/query_grammar.schema.json`](https://github.com/Identy-cloud/connordb/blob/main/docs/query_grammar.schema.json)
81
+ (also discoverable at runtime via `GrpcClient.get_grammar()`).
82
+
83
+ ### Agent-facing API (selected)
84
+
85
+ | Method | RPC | Notes |
86
+ |---|---|---|
87
+ | `insert(id, text, embedding=..., payload=..., collection=...)` | `EngineService.Insert` | Atomic multi-modal commit. |
88
+ | `insert_if_unchanged(id, expected_lsn, text, embedding=...)` | `EngineService.InsertIfLsnMatches` | CAS by LSN (ADR 0013). |
89
+ | `search_hybrid(query, k, include_text=...)` | `EngineService.SearchHybrid` | BM25 + dense + sparse RRF fused server-side (ADR 0004). |
90
+ | `wait_for_embed(lsn)` / `wait_for_projection(name, lsn)` | `EngineService.EmbedWait` / `WaitForProjection` | Read-your-writes watermarks (ADR 0005, 0010). |
91
+ | `snapshot_as_of(as_of_lsn=...)` | `EngineService.SnapshotAsOf` | Whole-tenant time travel (ADR 0014). |
92
+ | `subscribe(...)` | `EngineService.Subscribe` | Push-based change feed (ADR 0008). |
93
+ | `cluster_topology() -> ClusterTopology` | `ClusterService.ClusterTopology` | ADR 0016 Phase 7: epoch + leader + per-member view (`id`, `addr`, `in_sync`, `last_ack_lsn`). Target the cluster-internal port (`--cluster-port`), not `--grpc-port`. |
94
+ | `record_tool_call(tool_name, args, result, cited_lsns=...) -> int` | `EngineService.RecordToolCall` | ADR 0017 Phase A: tamper-evident record of an external tool invocation. Returns the LSN of the `ToolCall` causal frame. |
95
+ | `record_decision(decision_id, cited_lsns, summary) -> int` | `EngineService.RecordDecision` | ADR 0017 Phase A: agent decision with verified citations. Cross-tenant cites raise `PermissionDenied`. |
96
+ | `traceback(root_lsn, max_depth=5) -> TraceGraph` | `EngineService.Traceback` | ADR 0017 Phase A: walk the citation graph rooted at `root_lsn`. Returns `nodes` + `edges` in BFS order. |
97
+ | `endorse_row(target_lsn) -> int` | `EngineService.EndorseRow` | ADR 0018 Phase A: durably endorse another agent's row. Idempotent on `(target_lsn, caller_agent_id)`; self-endorse raises `PermissionDenied`. Returns the LSN of the Endorse belief frame. |
98
+ | `dispute_row(target_lsn, reason) -> int` | `EngineService.DisputeRow` | ADR 0018 Phase A: durably dispute another agent's row with a free-form reason (max 1024 bytes). Multiple disputes per row coexist by design. Returns the LSN of the Dispute belief frame. |
99
+ | `set_storage_mode(mode)` | `EngineService.SetStorageMode` | ADR 0019 Phase B: switch the tenant between `"rowstore"` and `"dual"`. `"field"` reserved for Phase C. |
100
+ | `field_insert(id, text, payload, vector=...)` | `EngineService.FieldInsert` | ADR 0019 §4: mirrored rowstore + field deformation under one fsync. Returns `row_lsn` + `field_lsn`. |
101
+ | `field_sample(region, k, basis_id=..., as_of_lsn=...)` | `EngineService.ExecutePlan` (FieldSample stage) | ADR 0020 §4: sample `k` coords from a `Region` (anchor / anchor_lsn / tile / union / intersection / difference). Returns samples + `aggregate_quality`. |
102
+ | `field_seal()` | `EngineService.FieldSeal` | ADR 0019 §4: freeze the current field epoch. |
103
+ | `field_evolve_basis(seed=...)` | `EngineService.FieldEvolveBasis` | ADR 0019 §4: explicit audited basis relearn. |
104
+
105
+ ### v1.8.0 — continuous-field storage (ADR 0019 / 0020 / 0021)
106
+
107
+ Tenants can opt into the continuous-field projection alongside the
108
+ rowstore. Five new methods, one new builder class, two new response
109
+ shapes:
110
+
111
+ ```python
112
+ from connordb import GrpcClient, Region
113
+
114
+ with GrpcClient("localhost:50051", api_key="…") as c:
115
+ # 1. Opt the tenant into dual-mode storage.
116
+ c.set_storage_mode("dual")
117
+
118
+ # 2. Insert a row — committed atomically into rowstore + field.
119
+ receipt = c.field_insert(
120
+ id="doc-1",
121
+ text="hello world",
122
+ payload=b"...", # opaque blob driving the deformation
123
+ vector=[0.1] * 384,
124
+ )
125
+ field_lsn = receipt["field_lsn"]
126
+
127
+ # 3. Sample the latent space addressed by a region AST.
128
+ region = Region.union([
129
+ Region.anchor_lsn(field_lsn, radius=0.25),
130
+ Region.tile("tile-east-1"),
131
+ ])
132
+ out = c.field_sample(region=region, k=16)
133
+ for s in out["samples"]:
134
+ print(s["field_coord_id"], s["quality"]["residual_norm"])
135
+ print(out["aggregate_quality"]) # ADR 0021 §6 summary
136
+ ```
137
+
138
+ `search_hybrid(...)` and `execute_plan(...)` both grew an optional
139
+ structured return path: pass `with_field_hits=True` to `search_hybrid`
140
+ when a plan included a `FieldSample` stage with `Disjoint` fusion;
141
+ `execute_plan` surfaces `field_hits` + `aggregate_quality`
142
+ automatically when the response carries them. Every `TextHit` /
143
+ `VectorHit` / `FieldHit` now exposes a `source` discriminator —
144
+ `"rowstore"` (default for legacy hits), `"field"`, or `"mixed_fused"`.
145
+
146
+ The `tenant_id` kwarg on every field-mode method is a forward-compat
147
+ explicit override (sent as `x-tenant-id` metadata). The engine
148
+ derives the active tenant from the API-key auth context; the
149
+ override is validated against it.
150
+
151
+ ### v1.3.0 — multi-agent belief primitives (ADR 0018 Phase A)
152
+
153
+ The audit chain becomes the team's shared belief register. Two new
154
+ methods let agents durably mark what they have validated together
155
+ without writing near-duplicate rows, and durably challenge what
156
+ they disagree on without silently overwriting it:
157
+
158
+ ```python
159
+ from connordb import GrpcClient
160
+
161
+ # Two agents both connected to the same tenant.
162
+ with GrpcClient("localhost:50051", api_key="…", agent_id="planner") as p, \
163
+ GrpcClient("localhost:50051", api_key="…", agent_id="verifier") as v:
164
+ # planner writes a fact.
165
+ lsn = p.insert(id="fact-1", text="the launch is Friday", embedding=...)
166
+ # verifier confirms it — endorsement is durable + tamper-evident.
167
+ p_endorse_lsn = v.endorse_row(target_lsn=lsn)
168
+ # ... or disputes it with reason.
169
+ # dispute = v.dispute_row(target_lsn=lsn, reason="actually Monday")
170
+ ```
171
+
172
+ Cross-tenant target → `NotFound`. Self-endorse → `PermissionDenied`.
173
+ Empty / oversize reason → `InvalidArgument`. Both RPCs require the
174
+ caller to identify via the `x-agent-id` metadata (set via the
175
+ `agent_id=` constructor arg). Disputes are sticky: the engine never
176
+ silently merges or overwrites — the kept row plus every dispute
177
+ remain together until a future operator resolution (Phase C).
178
+
179
+ ### v1.2.0 — causal tracing (ADR 0017 Phase A)
180
+
181
+ The audit chain becomes the public reasoning API. Three new methods
182
+ let an agent answer *"why did the swarm decide X at LSN L"* in one
183
+ RPC, with structural atomicity no external trace store can match:
184
+
185
+ ```python
186
+ from connordb import GrpcClient
187
+
188
+ with GrpcClient("localhost:50051", api_key="…") as c:
189
+ tool_lsn = c.record_tool_call(
190
+ "web_search",
191
+ args={"q": "connordb"},
192
+ result={"hits": 3},
193
+ )
194
+ decision_lsn = c.record_decision(
195
+ "deploy-friday",
196
+ cited_lsns=[tool_lsn],
197
+ summary="green retrieval, deploying",
198
+ )
199
+ graph = c.traceback(decision_lsn, max_depth=5)
200
+ for n in graph.nodes:
201
+ print(n.kind, n.lsn, n.label)
202
+ for e in graph.edges:
203
+ print(e.from_lsn, "→", e.to_lsn, f"({e.edge_type})")
204
+ ```
205
+
206
+ Cross-tenant cites are rejected with `PermissionDenied`; future
207
+ LSNs with `InvalidArgument`. The cited set is hash-bound into the
208
+ per-tenant SHA-256 audit chain — post-hoc re-citation breaks the
209
+ chain on `verify_audit_chain`.
210
+
211
+ ### v1.1.0 — cluster topology
212
+
213
+ ADR 0016 Phase 7 ships `cluster_topology()` returning a frozen
214
+ `ClusterTopology` dataclass with `epoch`, `leader_node_id`,
215
+ `leader_addr`, and `members: List[ClusterMember]`. Each member carries
216
+ `id`, `addr`, `in_sync`, and `last_ack_lsn`. Available on every node;
217
+ the leader's view is strongly consistent, a follower's is eventually
218
+ consistent.
219
+
220
+ ## v1.0.0 — the transport cut
221
+
222
+ The HTTP `Client` from v0.x was deleted in this release (Sprint 6, ADR 0006).
223
+ ConnorDB is machine-only post-pivot; the engine no longer exposes HTTP, so
224
+ the SDK does not either.
225
+
226
+ Migration: replace `connordb.Client(...)` with `connordb.GrpcClient(...)`.
227
+ Method names are mostly identical (`insert`, `delete`, `count`,
228
+ `search_hybrid`, …); see the docstring on each method for the new shape.
@@ -0,0 +1,199 @@
1
+ # connordb — Python SDK
2
+
3
+ Python SDK for [ConnorDB](https://github.com/Identy-cloud/connordb) — the system of record for agent memory. One ACID engine that gives a swarm of agents durable, auditable, tamper-evident memory: atomic multi-modal commit (row + BM25 + dense vector + sparse + typed payload under one fsync), in-process embed worker, LSN-watermark read-your-writes, server-side hybrid search. It also collapses the 5-service RAG stack (vector DB + document DB + embed service + queue + read-your-writes cache + retry + reranker) into that single engine. The SDK talks to a running engine over **gRPC** (the canonical machine transport, ADR 0006); the previous HTTP client was removed in v1.0.0.
4
+
5
+ A single `GrpcClient.insert(...)` call commits row + BM25 + dense vector + sparse + typed payload under one fsync; the embed worker runs server-side off the commit path; `wait_for_embed(lsn)` blocks on the LSN watermark; `search_hybrid(...)` fuses the indexes in one round-trip.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install connordb # full SDK (grpcio + protobuf)
11
+ ```
12
+
13
+ Or in dev mode:
14
+
15
+ ```bash
16
+ cd clients/connordb-py
17
+ pip install -e ".[dev]"
18
+ pytest
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```python
24
+ from connordb import GrpcClient
25
+
26
+ with GrpcClient(target="localhost:50051", api_key="…") as c:
27
+ # Atomic multi-modal commit.
28
+ lsn = c.insert(
29
+ id="doc-1",
30
+ text="ship the patch",
31
+ embedding=[0.1, 0.2, 0.3, 0.4], # match the engine's --embedding-dim
32
+ payload={"category": "urgent"},
33
+ collection="notes",
34
+ )
35
+
36
+ # Read-your-writes on the vector index.
37
+ c.wait_for_embed(lsn)
38
+
39
+ # Hybrid search: BM25 + dense fused via RRF.
40
+ hits = c.search_hybrid(query="patch", k=10, include_text=True)
41
+ for h in hits:
42
+ print(h["rank"], h["id"], h.get("distance") or h.get("score"))
43
+ ```
44
+
45
+ ## Surface
46
+
47
+ The full method list lives in [`src/connordb/grpc.py`](src/connordb/grpc.py).
48
+ The canonical wire contract is the proto under
49
+ [`proto/connordb/v1/`](https://github.com/Identy-cloud/connordb/tree/main/proto/connordb/v1)
50
+ and the QueryPlan AST in
51
+ [`docs/query_grammar.schema.json`](https://github.com/Identy-cloud/connordb/blob/main/docs/query_grammar.schema.json)
52
+ (also discoverable at runtime via `GrpcClient.get_grammar()`).
53
+
54
+ ### Agent-facing API (selected)
55
+
56
+ | Method | RPC | Notes |
57
+ |---|---|---|
58
+ | `insert(id, text, embedding=..., payload=..., collection=...)` | `EngineService.Insert` | Atomic multi-modal commit. |
59
+ | `insert_if_unchanged(id, expected_lsn, text, embedding=...)` | `EngineService.InsertIfLsnMatches` | CAS by LSN (ADR 0013). |
60
+ | `search_hybrid(query, k, include_text=...)` | `EngineService.SearchHybrid` | BM25 + dense + sparse RRF fused server-side (ADR 0004). |
61
+ | `wait_for_embed(lsn)` / `wait_for_projection(name, lsn)` | `EngineService.EmbedWait` / `WaitForProjection` | Read-your-writes watermarks (ADR 0005, 0010). |
62
+ | `snapshot_as_of(as_of_lsn=...)` | `EngineService.SnapshotAsOf` | Whole-tenant time travel (ADR 0014). |
63
+ | `subscribe(...)` | `EngineService.Subscribe` | Push-based change feed (ADR 0008). |
64
+ | `cluster_topology() -> ClusterTopology` | `ClusterService.ClusterTopology` | ADR 0016 Phase 7: epoch + leader + per-member view (`id`, `addr`, `in_sync`, `last_ack_lsn`). Target the cluster-internal port (`--cluster-port`), not `--grpc-port`. |
65
+ | `record_tool_call(tool_name, args, result, cited_lsns=...) -> int` | `EngineService.RecordToolCall` | ADR 0017 Phase A: tamper-evident record of an external tool invocation. Returns the LSN of the `ToolCall` causal frame. |
66
+ | `record_decision(decision_id, cited_lsns, summary) -> int` | `EngineService.RecordDecision` | ADR 0017 Phase A: agent decision with verified citations. Cross-tenant cites raise `PermissionDenied`. |
67
+ | `traceback(root_lsn, max_depth=5) -> TraceGraph` | `EngineService.Traceback` | ADR 0017 Phase A: walk the citation graph rooted at `root_lsn`. Returns `nodes` + `edges` in BFS order. |
68
+ | `endorse_row(target_lsn) -> int` | `EngineService.EndorseRow` | ADR 0018 Phase A: durably endorse another agent's row. Idempotent on `(target_lsn, caller_agent_id)`; self-endorse raises `PermissionDenied`. Returns the LSN of the Endorse belief frame. |
69
+ | `dispute_row(target_lsn, reason) -> int` | `EngineService.DisputeRow` | ADR 0018 Phase A: durably dispute another agent's row with a free-form reason (max 1024 bytes). Multiple disputes per row coexist by design. Returns the LSN of the Dispute belief frame. |
70
+ | `set_storage_mode(mode)` | `EngineService.SetStorageMode` | ADR 0019 Phase B: switch the tenant between `"rowstore"` and `"dual"`. `"field"` reserved for Phase C. |
71
+ | `field_insert(id, text, payload, vector=...)` | `EngineService.FieldInsert` | ADR 0019 §4: mirrored rowstore + field deformation under one fsync. Returns `row_lsn` + `field_lsn`. |
72
+ | `field_sample(region, k, basis_id=..., as_of_lsn=...)` | `EngineService.ExecutePlan` (FieldSample stage) | ADR 0020 §4: sample `k` coords from a `Region` (anchor / anchor_lsn / tile / union / intersection / difference). Returns samples + `aggregate_quality`. |
73
+ | `field_seal()` | `EngineService.FieldSeal` | ADR 0019 §4: freeze the current field epoch. |
74
+ | `field_evolve_basis(seed=...)` | `EngineService.FieldEvolveBasis` | ADR 0019 §4: explicit audited basis relearn. |
75
+
76
+ ### v1.8.0 — continuous-field storage (ADR 0019 / 0020 / 0021)
77
+
78
+ Tenants can opt into the continuous-field projection alongside the
79
+ rowstore. Five new methods, one new builder class, two new response
80
+ shapes:
81
+
82
+ ```python
83
+ from connordb import GrpcClient, Region
84
+
85
+ with GrpcClient("localhost:50051", api_key="…") as c:
86
+ # 1. Opt the tenant into dual-mode storage.
87
+ c.set_storage_mode("dual")
88
+
89
+ # 2. Insert a row — committed atomically into rowstore + field.
90
+ receipt = c.field_insert(
91
+ id="doc-1",
92
+ text="hello world",
93
+ payload=b"...", # opaque blob driving the deformation
94
+ vector=[0.1] * 384,
95
+ )
96
+ field_lsn = receipt["field_lsn"]
97
+
98
+ # 3. Sample the latent space addressed by a region AST.
99
+ region = Region.union([
100
+ Region.anchor_lsn(field_lsn, radius=0.25),
101
+ Region.tile("tile-east-1"),
102
+ ])
103
+ out = c.field_sample(region=region, k=16)
104
+ for s in out["samples"]:
105
+ print(s["field_coord_id"], s["quality"]["residual_norm"])
106
+ print(out["aggregate_quality"]) # ADR 0021 §6 summary
107
+ ```
108
+
109
+ `search_hybrid(...)` and `execute_plan(...)` both grew an optional
110
+ structured return path: pass `with_field_hits=True` to `search_hybrid`
111
+ when a plan included a `FieldSample` stage with `Disjoint` fusion;
112
+ `execute_plan` surfaces `field_hits` + `aggregate_quality`
113
+ automatically when the response carries them. Every `TextHit` /
114
+ `VectorHit` / `FieldHit` now exposes a `source` discriminator —
115
+ `"rowstore"` (default for legacy hits), `"field"`, or `"mixed_fused"`.
116
+
117
+ The `tenant_id` kwarg on every field-mode method is a forward-compat
118
+ explicit override (sent as `x-tenant-id` metadata). The engine
119
+ derives the active tenant from the API-key auth context; the
120
+ override is validated against it.
121
+
122
+ ### v1.3.0 — multi-agent belief primitives (ADR 0018 Phase A)
123
+
124
+ The audit chain becomes the team's shared belief register. Two new
125
+ methods let agents durably mark what they have validated together
126
+ without writing near-duplicate rows, and durably challenge what
127
+ they disagree on without silently overwriting it:
128
+
129
+ ```python
130
+ from connordb import GrpcClient
131
+
132
+ # Two agents both connected to the same tenant.
133
+ with GrpcClient("localhost:50051", api_key="…", agent_id="planner") as p, \
134
+ GrpcClient("localhost:50051", api_key="…", agent_id="verifier") as v:
135
+ # planner writes a fact.
136
+ lsn = p.insert(id="fact-1", text="the launch is Friday", embedding=...)
137
+ # verifier confirms it — endorsement is durable + tamper-evident.
138
+ p_endorse_lsn = v.endorse_row(target_lsn=lsn)
139
+ # ... or disputes it with reason.
140
+ # dispute = v.dispute_row(target_lsn=lsn, reason="actually Monday")
141
+ ```
142
+
143
+ Cross-tenant target → `NotFound`. Self-endorse → `PermissionDenied`.
144
+ Empty / oversize reason → `InvalidArgument`. Both RPCs require the
145
+ caller to identify via the `x-agent-id` metadata (set via the
146
+ `agent_id=` constructor arg). Disputes are sticky: the engine never
147
+ silently merges or overwrites — the kept row plus every dispute
148
+ remain together until a future operator resolution (Phase C).
149
+
150
+ ### v1.2.0 — causal tracing (ADR 0017 Phase A)
151
+
152
+ The audit chain becomes the public reasoning API. Three new methods
153
+ let an agent answer *"why did the swarm decide X at LSN L"* in one
154
+ RPC, with structural atomicity no external trace store can match:
155
+
156
+ ```python
157
+ from connordb import GrpcClient
158
+
159
+ with GrpcClient("localhost:50051", api_key="…") as c:
160
+ tool_lsn = c.record_tool_call(
161
+ "web_search",
162
+ args={"q": "connordb"},
163
+ result={"hits": 3},
164
+ )
165
+ decision_lsn = c.record_decision(
166
+ "deploy-friday",
167
+ cited_lsns=[tool_lsn],
168
+ summary="green retrieval, deploying",
169
+ )
170
+ graph = c.traceback(decision_lsn, max_depth=5)
171
+ for n in graph.nodes:
172
+ print(n.kind, n.lsn, n.label)
173
+ for e in graph.edges:
174
+ print(e.from_lsn, "→", e.to_lsn, f"({e.edge_type})")
175
+ ```
176
+
177
+ Cross-tenant cites are rejected with `PermissionDenied`; future
178
+ LSNs with `InvalidArgument`. The cited set is hash-bound into the
179
+ per-tenant SHA-256 audit chain — post-hoc re-citation breaks the
180
+ chain on `verify_audit_chain`.
181
+
182
+ ### v1.1.0 — cluster topology
183
+
184
+ ADR 0016 Phase 7 ships `cluster_topology()` returning a frozen
185
+ `ClusterTopology` dataclass with `epoch`, `leader_node_id`,
186
+ `leader_addr`, and `members: List[ClusterMember]`. Each member carries
187
+ `id`, `addr`, `in_sync`, and `last_ack_lsn`. Available on every node;
188
+ the leader's view is strongly consistent, a follower's is eventually
189
+ consistent.
190
+
191
+ ## v1.0.0 — the transport cut
192
+
193
+ The HTTP `Client` from v0.x was deleted in this release (Sprint 6, ADR 0006).
194
+ ConnorDB is machine-only post-pivot; the engine no longer exposes HTTP, so
195
+ the SDK does not either.
196
+
197
+ Migration: replace `connordb.Client(...)` with `connordb.GrpcClient(...)`.
198
+ Method names are mostly identical (`insert`, `delete`, `count`,
199
+ `search_hybrid`, …); see the docstring on each method for the new shape.
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "connordb"
7
+ version = "1.8.1"
8
+ description = "Python SDK for the connordb engine (machine-only, gRPC). Atomic multi-modal commits, async embedding freshness, hybrid search."
9
+ readme = "README.md"
10
+ license = { text = "Proprietary" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "connordb contributors" }]
13
+ keywords = ["database", "vector", "search", "ai", "embedding"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: Other/Proprietary License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Database",
23
+ ]
24
+ dependencies = [
25
+ "grpcio>=1.62",
26
+ "protobuf>=4.25",
27
+ "typing-extensions>=4.8; python_version<'3.11'",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "pytest>=7.4",
33
+ "pytest-cov>=4.1",
34
+ "ruff>=0.1",
35
+ "grpcio-tools>=1.62",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/Identy-cloud/connordb"
40
+ Documentation = "https://github.com/Identy-cloud/connordb/tree/main/clients/connordb-py"
41
+ Issues = "https://github.com/Identy-cloud/connordb/issues"
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/connordb"]
45
+
46
+ [tool.ruff]
47
+ line-length = 100
48
+ target-version = "py310"
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = ["tests"]
52
+ python_files = "test_*.py"
53
+ addopts = "-ra --strict-markers"