Skip to content

Commit b6ae1c5

Browse files
committed
fix(client): validate empty properties list and language identifier in create_text_index
- raise ValueError when properties list is empty - validate language as Cypher identifier before DDL interpolation - replace two-step edge+node DELETE with DETACH DELETE in integration test (undirected MATCH returns each edge twice, causing duplicate DELETE failure) fix(notebooks): skip embedded install when COORDINODE_ADDR is set - 01_llama, 02_langchain: guard Rust build with COORDINODE_ADDR check so Docker/server stacks skip the redundant ~5 min Colab build step - 03_langgraph: use full uuid4().hex (32 chars) for session ID to avoid collision in concurrent demo sessions - 03_langgraph: auto-append LIMIT 20 in query_facts when query has no LIMIT clause, bounding LLM-generated query result size
1 parent 7534b14 commit b6ae1c5

5 files changed

Lines changed: 15 additions & 100 deletions

File tree

coordinode/coordinode/client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,8 +595,12 @@ async def create_text_index(
595595
prop_list = [properties]
596596
else:
597597
prop_list = list(properties)
598+
if not prop_list:
599+
raise ValueError("'properties' must contain at least one property name")
598600
for prop in prop_list:
599601
_validate_cypher_identifier(prop, "property")
602+
if language:
603+
_validate_cypher_identifier(language, "language")
600604
props_expr = ", ".join(prop_list)
601605
lang_clause = f" DEFAULT LANGUAGE {language}" if language else ""
602606
cypher = f"CREATE TEXT INDEX {name} ON :{label}({props_expr}){lang_clause}"

demo/notebooks/01_llama_index_property_graph.ipynb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
"id": "b2c3d4e5-0001-0000-0000-000000000003",
4040
"metadata": {},
4141
"outputs": [],
42-
"source": "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:\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\")"
42+
"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\")"
44+
]
4345
},
4446
{
4547
"cell_type": "markdown",
@@ -318,4 +320,4 @@
318320
},
319321
"nbformat": 4,
320322
"nbformat_minor": 5
321-
}
323+
}

demo/notebooks/02_langchain_graph_chain.ipynb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
"id": "c3d4e5f6-0002-0000-0000-000000000003",
3737
"metadata": {},
3838
"outputs": [],
39-
"source": "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:\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\")"
39+
"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\")"
41+
]
4042
},
4143
{
4244
"cell_type": "markdown",
@@ -277,10 +279,7 @@
277279
"metadata": {},
278280
"outputs": [],
279281
"source": [
280-
"graph.query(\"MATCH (n) WHERE n.name ENDS WITH $tag DETACH DELETE n\", params={\"tag\": tag})\n",
281-
"print(\"Cleaned up\")\n",
282-
"graph.close()\n",
283-
"client.close() # injected client — owned by caller"
282+
"graph.query(\"MATCH (n) WHERE n.name ENDS WITH $tag DETACH DELETE n # atomically removes edges before deleting node\", params={\"tag\": tag})\nprint(\"Cleaned up\")\ngraph.close()\nclient.close() # injected client — owned by caller"
284283
]
285284
}
286285
],
@@ -296,4 +295,4 @@
296295
},
297296
"nbformat": 4,
298297
"nbformat_minor": 5
299-
}
298+
}

0 commit comments

Comments
 (0)