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.
- connordb-1.8.1/.gitignore +85 -0
- connordb-1.8.1/PKG-INFO +228 -0
- connordb-1.8.1/README.md +199 -0
- connordb-1.8.1/pyproject.toml +53 -0
- connordb-1.8.1/src/connordb/__init__.py +125 -0
- connordb-1.8.1/src/connordb/_pb/__init__.py +0 -0
- connordb-1.8.1/src/connordb/_pb/connordb/__init__.py +0 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/__init__.py +0 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/cluster_pb2.py +96 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/cluster_pb2_grpc.py +842 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/collections_pb2.py +81 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/collections_pb2_grpc.py +24 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/common_pb2.py +38 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/common_pb2_grpc.py +24 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/embed_pb2.py +46 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/embed_pb2_grpc.py +24 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/engine_pb2.py +281 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/engine_pb2_grpc.py +3056 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/grammar_pb2.py +40 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/grammar_pb2_grpc.py +100 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/replication_pb2.py +51 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/replication_pb2_grpc.py +157 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/snapshots_pb2.py +46 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/snapshots_pb2_grpc.py +24 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/types_pb2.py +60 -0
- connordb-1.8.1/src/connordb/_pb/connordb/v1/types_pb2_grpc.py +24 -0
- connordb-1.8.1/src/connordb/errors.py +255 -0
- connordb-1.8.1/src/connordb/grpc.py +2775 -0
- connordb-1.8.1/src/connordb/retry.py +86 -0
- connordb-1.8.1/tests/conftest.py +13 -0
- connordb-1.8.1/tests/test_errors.py +211 -0
- connordb-1.8.1/tests/test_grpc_client.py +353 -0
- 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
|
connordb-1.8.1/PKG-INFO
ADDED
|
@@ -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.
|
connordb-1.8.1/README.md
ADDED
|
@@ -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"
|