Skip to content

Commit 4b0274b

Browse files
committed
feat(sdk): update for coordinode-server v0.4.1
BREAKING CHANGE: HybridTextVectorSearch RPC removed upstream. - Bump coordinode-rs submodule to v0.4.1 - Bump proto submodule to eb472a4 (HybridTextVectorSearch removed) - Remove CoordinodeClient.hybrid_text_vector_search (async + sync) — replaced by Cypher hybrid scoring (rrf_score / hybrid_score / text_score) - Drop matching integration test - Bump docker-compose image tag to 0.4.1 (root + demo) - Re-execute all 4 demo notebooks against v0.4.1 Closes #46
1 parent bf14482 commit 4b0274b

10 files changed

Lines changed: 914 additions & 242 deletions

File tree

coordinode-rs

Submodule coordinode-rs updated 72 files

coordinode/coordinode/client.py

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -776,64 +776,6 @@ async def text_search(
776776
resp = await self._text_stub.TextSearch(req, timeout=self._timeout)
777777
return [TextResult(r) for r in resp.results]
778778

779-
async def hybrid_text_vector_search(
780-
self,
781-
label: str,
782-
text_query: str,
783-
vector: Sequence[float],
784-
*,
785-
limit: int = 10,
786-
text_weight: float = 0.5,
787-
vector_weight: float = 0.5,
788-
vector_property: str = "embedding",
789-
) -> list[HybridResult]:
790-
"""Fuse BM25 text search and cosine vector search using Reciprocal Rank Fusion (RRF).
791-
792-
Runs text and vector searches independently, then combines their ranked
793-
lists::
794-
795-
rrf_score(node) = text_weight / (60 + rank_text)
796-
+ vector_weight / (60 + rank_vec)
797-
798-
Args:
799-
label: Node label to search (e.g. ``"Article"``).
800-
text_query: Full-text query string (same syntax as :meth:`text_search`).
801-
vector: Query embedding vector. Must match the dimensionality stored
802-
in *vector_property*.
803-
limit: Maximum fused results to return (default 10). The server may
804-
apply its own upper bound; pass a reasonable value (e.g. ≤ 1000).
805-
text_weight: Weight for the BM25 component (default 0.5).
806-
vector_weight: Weight for the cosine component (default 0.5).
807-
vector_property: Node property containing the embedding (default
808-
``"embedding"``).
809-
810-
Returns:
811-
List of :class:`HybridResult` ordered by RRF score descending.
812-
813-
Note:
814-
A full-text index covering *label* **must exist** before calling this
815-
method — create one with :meth:`create_text_index` or a
816-
``CREATE TEXT INDEX`` Cypher statement. Calling this method on a
817-
label without a text index returns an empty list.
818-
"""
819-
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1:
820-
raise ValueError(f"limit must be an integer >= 1, got {limit!r}.")
821-
from coordinode._proto.coordinode.v1.query.text_pb2 import ( # type: ignore[import]
822-
HybridTextVectorSearchRequest,
823-
)
824-
825-
req = HybridTextVectorSearchRequest(
826-
label=label,
827-
text_query=text_query,
828-
vector=[float(v) for v in vector],
829-
limit=limit,
830-
text_weight=text_weight,
831-
vector_weight=vector_weight,
832-
vector_property=vector_property,
833-
)
834-
resp = await self._text_stub.HybridTextVectorSearch(req, timeout=self._timeout)
835-
return [HybridResult(r) for r in resp.results]
836-
837779
async def health(self) -> bool:
838780
from coordinode._proto.coordinode.v1.health.health_pb2 import ( # type: ignore[import]
839781
HealthCheckRequest,
@@ -1016,30 +958,6 @@ def text_search(
1016958
"""Run a full-text BM25 search over all indexed text properties for *label*."""
1017959
return self._run(self._async.text_search(label, query, limit=limit, fuzzy=fuzzy, language=language))
1018960

1019-
def hybrid_text_vector_search(
1020-
self,
1021-
label: str,
1022-
text_query: str,
1023-
vector: Sequence[float],
1024-
*,
1025-
limit: int = 10,
1026-
text_weight: float = 0.5,
1027-
vector_weight: float = 0.5,
1028-
vector_property: str = "embedding",
1029-
) -> list[HybridResult]:
1030-
"""Fuse BM25 text search and cosine vector search using RRF ranking."""
1031-
return self._run(
1032-
self._async.hybrid_text_vector_search(
1033-
label,
1034-
text_query,
1035-
vector,
1036-
limit=limit,
1037-
text_weight=text_weight,
1038-
vector_weight=vector_weight,
1039-
vector_property=vector_property,
1040-
)
1041-
)
1042-
1043961
def health(self) -> bool:
1044962
return self._run(self._async.health())
1045963

demo/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
services:
22
coordinode:
33
# Keep version in sync with root docker-compose.yml
4-
image: ghcr.io/structured-world/coordinode:0.3.17
4+
image: ghcr.io/structured-world/coordinode:0.4.1
55
container_name: demo-coordinode
66
ports:
77
- "127.0.0.1:37080:7080" # gRPC (native API) — localhost-only

demo/notebooks/00_seed_data.ipynb

Lines changed: 184 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,34 @@
4646
},
4747
{
4848
"cell_type": "code",
49-
"execution_count": null,
49+
"execution_count": 1,
5050
"id": "a1b2c3d4-0000-0000-0000-000000000003",
51-
"metadata": {},
52-
"outputs": [],
51+
"metadata": {
52+
"execution": {
53+
"iopub.execute_input": "2026-04-19T10:04:06.039243Z",
54+
"iopub.status.busy": "2026-04-19T10:04:06.039172Z",
55+
"iopub.status.idle": "2026-04-19T10:04:06.970761Z",
56+
"shell.execute_reply": "2026-04-19T10:04:06.970069Z"
57+
}
58+
},
59+
"outputs": [
60+
{
61+
"name": "stdout",
62+
"output_type": "stream",
63+
"text": [
64+
"Ready\n"
65+
]
66+
},
67+
{
68+
"name": "stderr",
69+
"output_type": "stream",
70+
"text": [
71+
"\n",
72+
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m24.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n",
73+
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip3 install --upgrade pip\u001b[0m\n"
74+
]
75+
}
76+
],
5377
"source": [
5478
"import os, sys, subprocess\n",
5579
"\n",
@@ -129,14 +153,35 @@
129153
"cell_type": "markdown",
130154
"id": "a1b2c3d4-0000-0000-0000-000000000004",
131155
"metadata": {},
132-
"source": "## Connect to CoordiNode\n\n- **Colab**: uses `LocalClient(\":memory:\")` — in-process embedded engine, no server required.\n- **Local with server**: connects to an existing CoordiNode on port 7080 (set `COORDINODE_ADDR` to override).\n- **Local without server**: falls back to `coordinode-embedded` if already installed (see [coordinode-embedded](https://github.com/structured-world/coordinode-python/tree/main/coordinode-embedded)); otherwise shows a `RuntimeError` with install instructions."
156+
"source": [
157+
"## Connect to CoordiNode\n",
158+
"\n",
159+
"- **Colab**: uses `LocalClient(\":memory:\")` — in-process embedded engine, no server required.\n",
160+
"- **Local with server**: connects to an existing CoordiNode on port 7080 (set `COORDINODE_ADDR` to override).\n",
161+
"- **Local without server**: falls back to `coordinode-embedded` if already installed (see [coordinode-embedded](https://github.com/structured-world/coordinode-python/tree/main/coordinode-embedded)); otherwise shows a `RuntimeError` with install instructions."
162+
]
133163
},
134164
{
135165
"cell_type": "code",
136-
"execution_count": null,
166+
"execution_count": 2,
137167
"id": "a1b2c3d4-0000-0000-0000-000000000005",
138-
"metadata": {},
139-
"outputs": [],
168+
"metadata": {
169+
"execution": {
170+
"iopub.execute_input": "2026-04-19T10:04:06.972780Z",
171+
"iopub.status.busy": "2026-04-19T10:04:06.972654Z",
172+
"iopub.status.idle": "2026-04-19T10:04:07.012113Z",
173+
"shell.execute_reply": "2026-04-19T10:04:07.011321Z"
174+
}
175+
},
176+
"outputs": [
177+
{
178+
"name": "stdout",
179+
"output_type": "stream",
180+
"text": [
181+
"Connected to 127.0.0.1:37080\n"
182+
]
183+
}
184+
],
140185
"source": [
141186
"import os, socket\n",
142187
"\n",
@@ -196,10 +241,26 @@
196241
},
197242
{
198243
"cell_type": "code",
199-
"execution_count": null,
244+
"execution_count": 3,
200245
"id": "a1b2c3d4-0000-0000-0000-000000000007",
201-
"metadata": {},
202-
"outputs": [],
246+
"metadata": {
247+
"execution": {
248+
"iopub.execute_input": "2026-04-19T10:04:07.014105Z",
249+
"iopub.status.busy": "2026-04-19T10:04:07.013909Z",
250+
"iopub.status.idle": "2026-04-19T10:04:07.020088Z",
251+
"shell.execute_reply": "2026-04-19T10:04:07.019603Z"
252+
}
253+
},
254+
"outputs": [
255+
{
256+
"name": "stdout",
257+
"output_type": "stream",
258+
"text": [
259+
"Using DEMO_TAG: seed_data_7cfe797a\n",
260+
"Previous demo data removed\n"
261+
]
262+
}
263+
],
203264
"source": [
204265
"import uuid\n",
205266
"\n",
@@ -225,10 +286,27 @@
225286
},
226287
{
227288
"cell_type": "code",
228-
"execution_count": null,
289+
"execution_count": 4,
229290
"id": "a1b2c3d4-0000-0000-0000-000000000009",
230-
"metadata": {},
231-
"outputs": [],
291+
"metadata": {
292+
"execution": {
293+
"iopub.execute_input": "2026-04-19T10:04:07.021785Z",
294+
"iopub.status.busy": "2026-04-19T10:04:07.021661Z",
295+
"iopub.status.idle": "2026-04-19T10:04:07.057106Z",
296+
"shell.execute_reply": "2026-04-19T10:04:07.056653Z"
297+
}
298+
},
299+
"outputs": [
300+
{
301+
"name": "stdout",
302+
"output_type": "stream",
303+
"text": [
304+
"Created 10 people\n",
305+
"Created 6 companies\n",
306+
"Created 8 technologies\n"
307+
]
308+
}
309+
],
232310
"source": [
233311
"# ── People ────────────────────────────────────────────────────────────────\n",
234312
"people = [\n",
@@ -302,10 +380,25 @@
302380
},
303381
{
304382
"cell_type": "code",
305-
"execution_count": null,
383+
"execution_count": 5,
306384
"id": "a1b2c3d4-0000-0000-0000-000000000011",
307-
"metadata": {},
308-
"outputs": [],
385+
"metadata": {
386+
"execution": {
387+
"iopub.execute_input": "2026-04-19T10:04:07.058969Z",
388+
"iopub.status.busy": "2026-04-19T10:04:07.058874Z",
389+
"iopub.status.idle": "2026-04-19T10:04:07.100906Z",
390+
"shell.execute_reply": "2026-04-19T10:04:07.100443Z"
391+
}
392+
},
393+
"outputs": [
394+
{
395+
"name": "stdout",
396+
"output_type": "stream",
397+
"text": [
398+
"Created 34 relationships\n"
399+
]
400+
}
401+
],
309402
"source": [
310403
"edges = [\n",
311404
" # WORKS_AT\n",
@@ -391,10 +484,38 @@
391484
},
392485
{
393486
"cell_type": "code",
394-
"execution_count": null,
487+
"execution_count": 6,
395488
"id": "a1b2c3d4-0000-0000-0000-000000000013",
396-
"metadata": {},
397-
"outputs": [],
489+
"metadata": {
490+
"execution": {
491+
"iopub.execute_input": "2026-04-19T10:04:07.102456Z",
492+
"iopub.status.busy": "2026-04-19T10:04:07.102333Z",
493+
"iopub.status.idle": "2026-04-19T10:04:07.110337Z",
494+
"shell.execute_reply": "2026-04-19T10:04:07.109926Z"
495+
}
496+
},
497+
"outputs": [
498+
{
499+
"name": "stdout",
500+
"output_type": "stream",
501+
"text": [
502+
"Node counts:\n",
503+
" Person 10\n",
504+
" Company 6\n",
505+
" Technology 8\n",
506+
"\n",
507+
"Relationship counts:\n",
508+
" WORKS_AT 10\n",
509+
" KNOWS 6\n",
510+
" RESEARCHES 6\n",
511+
" USES 5\n",
512+
" BUILDS_ON 4\n",
513+
" FOUNDED 1\n",
514+
" CO_FOUNDED 1\n",
515+
" ACQUIRED 1\n"
516+
]
517+
}
518+
],
398519
"source": [
399520
"from collections import Counter\n",
400521
"\n",
@@ -419,10 +540,41 @@
419540
},
420541
{
421542
"cell_type": "code",
422-
"execution_count": null,
543+
"execution_count": 7,
423544
"id": "a1b2c3d4-0000-0000-0000-000000000014",
424-
"metadata": {},
425-
"outputs": [],
545+
"metadata": {
546+
"execution": {
547+
"iopub.execute_input": "2026-04-19T10:04:07.112104Z",
548+
"iopub.status.busy": "2026-04-19T10:04:07.111970Z",
549+
"iopub.status.idle": "2026-04-19T10:04:07.118744Z",
550+
"shell.execute_reply": "2026-04-19T10:04:07.118356Z"
551+
}
552+
},
553+
"outputs": [
554+
{
555+
"name": "stdout",
556+
"output_type": "stream",
557+
"text": [
558+
"=== Who works at Synthex? ===\n",
559+
" Carol Smith — Founder & CEO\n",
560+
" Eva Müller — Systems Architect\n",
561+
" Henry Rossi — CTO\n",
562+
"\n",
563+
"=== What does Synthex use? ===\n",
564+
" Knowledge Graph\n",
565+
" Vector Database\n",
566+
" RAG\n",
567+
"\n",
568+
"=== GraphRAG dependency chain ===\n",
569+
" → Knowledge Graph\n",
570+
" → RAG\n",
571+
" → Vector Database\n",
572+
"\n",
573+
"✓ Demo data seeded.\n",
574+
"To query it from notebooks 01–03, connect them to the same CoordiNode server (COORDINODE_ADDR).\n"
575+
]
576+
}
577+
],
426578
"source": [
427579
"print(\"=== Who works at Synthex? ===\")\n",
428580
"rows = client.cypher(\n",
@@ -462,7 +614,16 @@
462614
"name": "python3"
463615
},
464616
"language_info": {
465-
"name": "python"
617+
"codemirror_mode": {
618+
"name": "ipython",
619+
"version": 3
620+
},
621+
"file_extension": ".py",
622+
"mimetype": "text/x-python",
623+
"name": "python",
624+
"nbconvert_exporter": "python",
625+
"pygments_lexer": "ipython3",
626+
"version": "3.11.9"
466627
}
467628
},
468629
"nbformat": 4,

0 commit comments

Comments
 (0)