Skip to content

Commit 14bd402

Browse files
committed
fix(notebooks,sdk): defer GRPC_PORT parsing and document index API
- defer int(GRPC_PORT) evaluation to else-branch in 00_seed_data and 03_langgraph notebooks so a malformed COORDINODE_PORT does not raise when COORDINODE_ADDR is already set - add pinning rationale comment to pip install cells in 01_llama and 02_langchain: coordinode-embedded is pinned to a git commit (Rust build); pure-Python packages are intentionally unpinned - clarify FTS test section comment: tests exercise explicit create/drop lifecycle; test_text_search_empty_for_unindexed_label must not create an index - add docstrings to create_text_index and drop_text_index documenting the Cypher identifier restriction and backtick-escaping alternative
1 parent 61eb906 commit 14bd402

6 files changed

Lines changed: 211 additions & 50 deletions

File tree

coordinode/coordinode/client.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,19 @@ async def create_text_index(
573573
"""Create a full-text (BM25) index on one or more node properties.
574574
575575
Args:
576-
name: Unique index name (e.g. ``"article_body"``).
577-
label: Node label to index (e.g. ``"Article"``).
576+
name: Unique index name (e.g. ``"article_body"``). Must be a
577+
simple Cypher identifier: letters, digits, and underscores only,
578+
starting with a letter or underscore. Names with dashes or
579+
spaces are not supported by this method; use raw :meth:`cypher`
580+
with backtick-escaped identifiers instead.
581+
label: Node label to index (e.g. ``"Article"``). Same identifier
582+
restrictions as *name* apply.
578583
properties: Property name or list of property names to index
579-
(e.g. ``"body"`` or ``["title", "body"]``).
584+
(e.g. ``"body"`` or ``["title", "body"]``). Same identifier
585+
restrictions apply.
580586
language: Default stemming/tokenization language (e.g. ``"english"``,
581587
``"russian"``). Empty string uses the server default
582-
(``"english"``).
588+
(``"english"``). Same identifier restrictions apply.
583589
584590
Returns:
585591
:class:`TextIndexInfo` with index metadata and document count.
@@ -614,6 +620,9 @@ async def drop_text_index(self, name: str) -> None:
614620
615621
Args:
616622
name: Index name previously passed to :meth:`create_text_index`.
623+
Must be a simple Cypher identifier (letters, digits, underscores).
624+
Use raw :meth:`cypher` with backtick-escaped identifiers for names
625+
that contain dashes or spaces.
617626
618627
Example::
619628

demo/notebooks/00_seed_data.ipynb

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,33 +76,37 @@
7676
" return False\n",
7777
"\n",
7878
"\n",
79-
"GRPC_PORT = int(os.environ.get(\"COORDINODE_PORT\", \"7080\"))\n",
80-
"\n",
8179
"if os.environ.get(\"COORDINODE_ADDR\"):\n",
8280
" COORDINODE_ADDR = os.environ[\"COORDINODE_ADDR\"]\n",
8381
" from coordinode import CoordinodeClient\n",
8482
"\n",
8583
" client = CoordinodeClient(COORDINODE_ADDR)\n",
8684
" print(f\"Connected to {COORDINODE_ADDR}\")\n",
87-
"elif _port_open(GRPC_PORT):\n",
88-
" COORDINODE_ADDR = f\"localhost:{GRPC_PORT}\"\n",
89-
" from coordinode import CoordinodeClient\n",
90-
"\n",
91-
" client = CoordinodeClient(COORDINODE_ADDR)\n",
92-
" print(f\"Connected to {COORDINODE_ADDR}\")\n",
9385
"else:\n",
94-
" # No server available — use the embedded in-process engine.\n",
9586
" try:\n",
96-
" from coordinode_embedded import LocalClient\n",
97-
" except ImportError as exc:\n",
98-
" raise RuntimeError(\n",
99-
" \"coordinode-embedded is not installed. \"\n",
100-
" \"Run: pip install git+https://github.com/structured-world/coordinode-python.git@bd8d0dc#subdirectory=coordinode-embedded\"\n",
101-
" \" — or start a CoordiNode server and set COORDINODE_ADDR.\"\n",
102-
" ) from exc\n",
103-
"\n",
104-
" client = LocalClient(\":memory:\")\n",
105-
" print(\"Using embedded LocalClient (in-process)\")"
87+
" grpc_port = int(os.environ.get(\"COORDINODE_PORT\", \"7080\"))\n",
88+
" except ValueError as exc:\n",
89+
" raise RuntimeError(\"COORDINODE_PORT must be an integer\") from exc\n",
90+
"\n",
91+
" if _port_open(grpc_port):\n",
92+
" COORDINODE_ADDR = f\"localhost:{grpc_port}\"\n",
93+
" from coordinode import CoordinodeClient\n",
94+
"\n",
95+
" client = CoordinodeClient(COORDINODE_ADDR)\n",
96+
" print(f\"Connected to {COORDINODE_ADDR}\")\n",
97+
" else:\n",
98+
" # No server available — use the embedded in-process engine.\n",
99+
" try:\n",
100+
" from coordinode_embedded import LocalClient\n",
101+
" except ImportError as exc:\n",
102+
" raise RuntimeError(\n",
103+
" \"coordinode-embedded is not installed. \"\n",
104+
" \"Run: pip install git+https://github.com/structured-world/coordinode-python.git@bd8d0dc#subdirectory=coordinode-embedded\"\n",
105+
" \" — or start a CoordiNode server and set COORDINODE_ADDR.\"\n",
106+
" ) from exc\n",
107+
"\n",
108+
" client = LocalClient(\":memory:\")\n",
109+
" print(\"Using embedded LocalClient (in-process)\")"
106110
]
107111
},
108112
{

demo/notebooks/01_llama_index_property_graph.ipynb

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,80 @@
4040
"metadata": {},
4141
"outputs": [],
4242
"source": [
43-
"import os, sys, subprocess\n\nIN_COLAB = \"google.colab\" in sys.modules\n\n# Install coordinode-embedded in Colab only (requires Rust build).\nif IN_COLAB and not os.environ.get(\"COORDINODE_ADDR\"):\n # Install Rust toolchain via rustup (https://rustup.rs).\n # Colab's apt packages ship rustc ≤1.75, which cannot build coordinode-embedded\n # (requires Rust ≥1.80 for maturin/pyo3). apt-get is not a viable alternative here.\n # Download the installer to a temp file and execute it explicitly — this avoids\n # piping remote content directly into a shell while maintaining HTTPS/TLS security\n # through Python's default ssl context (cert-verified, TLS 1.2+).\n # SHA256 pinning of rustup-init is intentionally omitted: rustup.rs does not\n # publish a stable per-release checksum for sh.rustup.rs itself (only for\n # platform-specific rustup-init binaries), and pinning a hash here would break\n # silently on every rustup release. The HTTPS/TLS verification + temp-file\n # execution (not piped to shell) is the rustup team's recommended trust model.\n # No additional env-var gate (e.g. COORDINODE_ENABLE_RUSTUP) is needed:\n # the `IN_COLAB` check above already ensures this block never runs outside\n # Colab sessions, so there is no risk of unintentional execution in local\n # or server environments.\n import ssl as _ssl, tempfile as _tmp, urllib.request as _ur\n\n _ctx = _ssl.create_default_context()\n with _tmp.NamedTemporaryFile(mode=\"wb\", suffix=\".sh\", delete=False) as _f:\n with _ur.urlopen(\"https://sh.rustup.rs\", context=_ctx, timeout=30) as _r:\n _f.write(_r.read())\n _rustup_path = _f.name\n try:\n subprocess.run([\"/bin/sh\", _rustup_path, \"-s\", \"--\", \"-y\", \"-q\"], check=True, timeout=300)\n finally:\n os.unlink(_rustup_path)\n # Add cargo to PATH so maturin/pip can find it.\n _cargo_bin = os.path.expanduser(\"~/.cargo/bin\")\n os.environ[\"PATH\"] = f\"{_cargo_bin}{os.pathsep}{os.environ.get('PATH', '')}\"\n subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"maturin\"], check=True, timeout=300)\n subprocess.run(\n [\n sys.executable,\n \"-m\",\n \"pip\",\n \"install\",\n \"-q\",\n \"git+https://github.com/structured-world/coordinode-python.git@bd8d0dc#subdirectory=coordinode-embedded\",\n ],\n check=True,\n timeout=600,\n )\n\nsubprocess.run(\n [\n sys.executable,\n \"-m\",\n \"pip\",\n \"install\",\n \"-q\",\n \"coordinode\",\n \"llama-index-graph-stores-coordinode\",\n \"llama-index-core\",\n \"nest_asyncio\",\n ],\n check=True,\n timeout=300,\n)\n\nimport nest_asyncio\n\nnest_asyncio.apply()\n\nprint(\"SDK installed\")"
43+
"import os, sys, subprocess\n",
44+
"\n",
45+
"IN_COLAB = \"google.colab\" in sys.modules\n",
46+
"\n",
47+
"# Install coordinode-embedded in Colab only (requires Rust build).\n",
48+
"if IN_COLAB and not os.environ.get(\"COORDINODE_ADDR\"):\n",
49+
" # Install Rust toolchain via rustup (https://rustup.rs).\n",
50+
" # Colab's apt packages ship rustc ≤1.75, which cannot build coordinode-embedded\n",
51+
" # (requires Rust ≥1.80 for maturin/pyo3). apt-get is not a viable alternative here.\n",
52+
" # Download the installer to a temp file and execute it explicitly — this avoids\n",
53+
" # piping remote content directly into a shell while maintaining HTTPS/TLS security\n",
54+
" # through Python's default ssl context (cert-verified, TLS 1.2+).\n",
55+
" # SHA256 pinning of rustup-init is intentionally omitted: rustup.rs does not\n",
56+
" # publish a stable per-release checksum for sh.rustup.rs itself (only for\n",
57+
" # platform-specific rustup-init binaries), and pinning a hash here would break\n",
58+
" # silently on every rustup release. The HTTPS/TLS verification + temp-file\n",
59+
" # execution (not piped to shell) is the rustup team's recommended trust model.\n",
60+
" # No additional env-var gate (e.g. COORDINODE_ENABLE_RUSTUP) is needed:\n",
61+
" # the `IN_COLAB` check above already ensures this block never runs outside\n",
62+
" # Colab sessions, so there is no risk of unintentional execution in local\n",
63+
" # or server environments.\n",
64+
" import ssl as _ssl, tempfile as _tmp, urllib.request as _ur\n",
65+
"\n",
66+
" _ctx = _ssl.create_default_context()\n",
67+
" with _tmp.NamedTemporaryFile(mode=\"wb\", suffix=\".sh\", delete=False) as _f:\n",
68+
" with _ur.urlopen(\"https://sh.rustup.rs\", context=_ctx, timeout=30) as _r:\n",
69+
" _f.write(_r.read())\n",
70+
" _rustup_path = _f.name\n",
71+
" try:\n",
72+
" subprocess.run([\"/bin/sh\", _rustup_path, \"-s\", \"--\", \"-y\", \"-q\"], check=True, timeout=300)\n",
73+
" finally:\n",
74+
" os.unlink(_rustup_path)\n",
75+
" # Add cargo to PATH so maturin/pip can find it.\n",
76+
" _cargo_bin = os.path.expanduser(\"~/.cargo/bin\")\n",
77+
" os.environ[\"PATH\"] = f\"{_cargo_bin}{os.pathsep}{os.environ.get('PATH', '')}\"\n",
78+
" subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"maturin\"], check=True, timeout=300)\n",
79+
" subprocess.run(\n",
80+
" [\n",
81+
" sys.executable,\n",
82+
" \"-m\",\n",
83+
" \"pip\",\n",
84+
" \"install\",\n",
85+
" \"-q\",\n",
86+
" \"git+https://github.com/structured-world/coordinode-python.git@bd8d0dc#subdirectory=coordinode-embedded\",\n",
87+
" ],\n",
88+
" check=True,\n",
89+
" timeout=600,\n",
90+
" )\n",
91+
"\n",
92+
"# coordinode-embedded is pinned to a specific git commit because it requires a Rust\n",
93+
"# build (maturin/pyo3) and the embedded engine must match the Python SDK version.\n",
94+
"# The remaining packages (coordinode, llama-index, etc.) are installed without pins:\n",
95+
"# they are pure Python, release frequently, and pip resolves a compatible version.\n",
96+
"subprocess.run(\n",
97+
" [\n",
98+
" sys.executable,\n",
99+
" \"-m\",\n",
100+
" \"pip\",\n",
101+
" \"install\",\n",
102+
" \"-q\",\n",
103+
" \"coordinode\",\n",
104+
" \"llama-index-graph-stores-coordinode\",\n",
105+
" \"llama-index-core\",\n",
106+
" \"nest_asyncio\",\n",
107+
" ],\n",
108+
" check=True,\n",
109+
" timeout=300,\n",
110+
")\n",
111+
"\n",
112+
"import nest_asyncio\n",
113+
"\n",
114+
"nest_asyncio.apply()\n",
115+
"\n",
116+
"print(\"SDK installed\")"
44117
]
45118
},
46119
{

demo/notebooks/02_langchain_graph_chain.ipynb

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,77 @@
3737
"metadata": {},
3838
"outputs": [],
3939
"source": [
40-
"import os, sys, subprocess\n\nIN_COLAB = \"google.colab\" in sys.modules\n\n# Install coordinode-embedded in Colab only (requires Rust build).\nif IN_COLAB and not os.environ.get(\"COORDINODE_ADDR\"):\n # Install Rust toolchain via rustup (https://rustup.rs).\n # Colab's apt packages ship rustc ≤1.75, which cannot build coordinode-embedded\n # (requires Rust ≥1.80 for maturin/pyo3). apt-get is not a viable alternative here.\n # Download the installer to a temp file and execute it explicitly — this avoids\n # piping remote content directly into a shell while maintaining HTTPS/TLS security\n # through Python's default ssl context (cert-verified, TLS 1.2+).\n # SHA256 pinning of rustup-init is intentionally omitted: rustup.rs does not\n # publish a stable per-release checksum for sh.rustup.rs itself (only for\n # platform-specific rustup-init binaries), and pinning a hash here would break\n # silently on every rustup release. The HTTPS/TLS verification + temp-file\n # execution (not piped to shell) is the rustup team's recommended trust model.\n # No additional env-var gate (e.g. COORDINODE_ENABLE_RUSTUP) is needed:\n # the `IN_COLAB` check above already ensures this block never runs outside\n # Colab sessions, so there is no risk of unintentional execution in local\n # or server environments.\n import ssl as _ssl, tempfile as _tmp, urllib.request as _ur\n\n _ctx = _ssl.create_default_context()\n with _tmp.NamedTemporaryFile(mode=\"wb\", suffix=\".sh\", delete=False) as _f:\n with _ur.urlopen(\"https://sh.rustup.rs\", context=_ctx, timeout=30) as _r:\n _f.write(_r.read())\n _rustup_path = _f.name\n try:\n subprocess.run([\"/bin/sh\", _rustup_path, \"-s\", \"--\", \"-y\", \"-q\"], check=True, timeout=300)\n finally:\n os.unlink(_rustup_path)\n # Add cargo to PATH so maturin/pip can find it.\n _cargo_bin = os.path.expanduser(\"~/.cargo/bin\")\n os.environ[\"PATH\"] = f\"{_cargo_bin}{os.pathsep}{os.environ.get('PATH', '')}\"\n subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"maturin\"], check=True, timeout=300)\n subprocess.run(\n [\n sys.executable,\n \"-m\",\n \"pip\",\n \"install\",\n \"-q\",\n \"git+https://github.com/structured-world/coordinode-python.git@bd8d0dc#subdirectory=coordinode-embedded\",\n ],\n check=True,\n timeout=600,\n )\n\nsubprocess.run(\n [\n sys.executable,\n \"-m\",\n \"pip\",\n \"install\",\n \"-q\",\n \"coordinode\",\n \"langchain\",\n \"langchain-coordinode\",\n \"langchain-community\",\n \"langchain-openai\",\n ],\n check=True,\n timeout=300,\n)\n\nprint(\"SDK installed\")"
40+
"import os, sys, subprocess\n",
41+
"\n",
42+
"IN_COLAB = \"google.colab\" in sys.modules\n",
43+
"\n",
44+
"# Install coordinode-embedded in Colab only (requires Rust build).\n",
45+
"if IN_COLAB and not os.environ.get(\"COORDINODE_ADDR\"):\n",
46+
" # Install Rust toolchain via rustup (https://rustup.rs).\n",
47+
" # Colab's apt packages ship rustc ≤1.75, which cannot build coordinode-embedded\n",
48+
" # (requires Rust ≥1.80 for maturin/pyo3). apt-get is not a viable alternative here.\n",
49+
" # Download the installer to a temp file and execute it explicitly — this avoids\n",
50+
" # piping remote content directly into a shell while maintaining HTTPS/TLS security\n",
51+
" # through Python's default ssl context (cert-verified, TLS 1.2+).\n",
52+
" # SHA256 pinning of rustup-init is intentionally omitted: rustup.rs does not\n",
53+
" # publish a stable per-release checksum for sh.rustup.rs itself (only for\n",
54+
" # platform-specific rustup-init binaries), and pinning a hash here would break\n",
55+
" # silently on every rustup release. The HTTPS/TLS verification + temp-file\n",
56+
" # execution (not piped to shell) is the rustup team's recommended trust model.\n",
57+
" # No additional env-var gate (e.g. COORDINODE_ENABLE_RUSTUP) is needed:\n",
58+
" # the `IN_COLAB` check above already ensures this block never runs outside\n",
59+
" # Colab sessions, so there is no risk of unintentional execution in local\n",
60+
" # or server environments.\n",
61+
" import ssl as _ssl, tempfile as _tmp, urllib.request as _ur\n",
62+
"\n",
63+
" _ctx = _ssl.create_default_context()\n",
64+
" with _tmp.NamedTemporaryFile(mode=\"wb\", suffix=\".sh\", delete=False) as _f:\n",
65+
" with _ur.urlopen(\"https://sh.rustup.rs\", context=_ctx, timeout=30) as _r:\n",
66+
" _f.write(_r.read())\n",
67+
" _rustup_path = _f.name\n",
68+
" try:\n",
69+
" subprocess.run([\"/bin/sh\", _rustup_path, \"-s\", \"--\", \"-y\", \"-q\"], check=True, timeout=300)\n",
70+
" finally:\n",
71+
" os.unlink(_rustup_path)\n",
72+
" # Add cargo to PATH so maturin/pip can find it.\n",
73+
" _cargo_bin = os.path.expanduser(\"~/.cargo/bin\")\n",
74+
" os.environ[\"PATH\"] = f\"{_cargo_bin}{os.pathsep}{os.environ.get('PATH', '')}\"\n",
75+
" subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"maturin\"], check=True, timeout=300)\n",
76+
" subprocess.run(\n",
77+
" [\n",
78+
" sys.executable,\n",
79+
" \"-m\",\n",
80+
" \"pip\",\n",
81+
" \"install\",\n",
82+
" \"-q\",\n",
83+
" \"git+https://github.com/structured-world/coordinode-python.git@bd8d0dc#subdirectory=coordinode-embedded\",\n",
84+
" ],\n",
85+
" check=True,\n",
86+
" timeout=600,\n",
87+
" )\n",
88+
"\n",
89+
"# coordinode-embedded is pinned to a specific git commit because it requires a Rust\n",
90+
"# build (maturin/pyo3) and the embedded engine must match the Python SDK version.\n",
91+
"# The remaining packages (coordinode, LangChain, etc.) are installed without pins:\n",
92+
"# they are pure Python, release frequently, and pip resolves a compatible version.\n",
93+
"subprocess.run(\n",
94+
" [\n",
95+
" sys.executable,\n",
96+
" \"-m\",\n",
97+
" \"pip\",\n",
98+
" \"install\",\n",
99+
" \"-q\",\n",
100+
" \"coordinode\",\n",
101+
" \"langchain\",\n",
102+
" \"langchain-coordinode\",\n",
103+
" \"langchain-community\",\n",
104+
" \"langchain-openai\",\n",
105+
" ],\n",
106+
" check=True,\n",
107+
" timeout=300,\n",
108+
")\n",
109+
"\n",
110+
"print(\"SDK installed\")"
41111
]
42112
},
43113
{

0 commit comments

Comments
 (0)