Skip to content

Commit 92d10eb

Browse files
committed
fix(llama-index,client,build): prevent param collision, port conflict, and sed idempotency
- base.py: use indexed params (p0, p1, …) in get() to prevent collision when different property keys sanitize to the same name - client.py: raise ValueError when explicit port conflicts with port embedded in host string - Makefile: add PYTHON ?= python3; use $(PYTHON) -m grpc_tools.protoc - Makefile: tighten sed to match only v1. prefix so make proto is idempotent (repeated runs no longer double-rewrite imports) - _types.py: use X | Y union syntax in isinstance() (UP038) - test_sdk.py: drop bare f-string prefix (F541)
1 parent d09191d commit 92d10eb

5 files changed

Lines changed: 24 additions & 13 deletions

File tree

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
PROTO_SRC := proto
44
PROTO_OUT := coordinode/_proto
5+
PYTHON ?= python3
56

67
# Generate gRPC stubs from proto submodule into coordinode/_proto/
78
proto:
89
@echo "==> Generating proto stubs..."
910
@mkdir -p $(PROTO_OUT)
10-
python3 -m grpc_tools.protoc \
11+
$(PYTHON) -m grpc_tools.protoc \
1112
-I$(PROTO_SRC) \
1213
--python_out=$(PROTO_OUT) \
1314
--grpc_python_out=$(PROTO_OUT) \
@@ -19,7 +20,7 @@ proto:
1920
@# sed -i.bak is portable: macOS needs empty-string backup arg, GNU sed uses -i alone;
2021
@# using .bak suffix works on both, then we clean up the backup files.
2122
@find $(PROTO_OUT) -name '*.py' -exec sed -i.bak \
22-
's/from coordinode\./from coordinode._proto.coordinode./g' {} \;
23+
's/from coordinode\.v1\./from coordinode._proto.coordinode.v1./g' {} \;
2324
@find $(PROTO_OUT) -name '*.py.bak' -delete
2425
@echo "==> Proto generation complete: $(PROTO_OUT)/"
2526

coordinode/_types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def to_property_value(py_val: PyValue) -> Any:
3939
# This package requires Python >=3.11, so no tuple-of-types workaround needed.
4040
# bool is a subclass of int, so exclude it explicitly — [True, False] must
4141
# not be serialised as a Vector of 1.0/0.0 but as a PropertyList.
42-
if py_val and all(isinstance(v, (int, float)) and not isinstance(v, bool) for v in py_val):
42+
if py_val and all(isinstance(v, int | float) and not isinstance(v, bool) for v in py_val):
4343
vec = Vector(values=[float(v) for v in py_val])
4444
pv.vector_value.CopyFrom(vec)
4545
else:

coordinode/client.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,14 @@ def __init__(
116116
# IPv6 addresses, avoiding the ambiguity of rsplit(":", 1) on "::1".
117117
m = _HOST_PORT_RE.match(host)
118118
if m:
119-
host, port = m.group(1), int(m.group(2))
119+
parsed_port = int(m.group(2))
120+
if port != 7080 and port != parsed_port:
121+
raise ValueError(
122+
f"Conflicting ports: port={port!r} (argument) vs {parsed_port!r} "
123+
f"(embedded in host={host!r}). Specify the port in the host string "
124+
"only, or use the port argument only."
125+
)
126+
host, port = m.group(1), parsed_port
120127
self._host = host
121128
self._port = port
122129
self._tls = tls

llama-index-coordinode/llama_index/graph_stores/coordinode/base.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,16 @@ def get(
9191
node_id = str(row.get("_nid", ""))
9292
nodes.append(_node_result_to_labelled(node_id, node_data))
9393
elif properties:
94-
# Build param mapping: safe Cypher param name → original value.
95-
# Property keys may contain hyphens or other chars invalid in
96-
# Cypher parameter names; _cypher_param_name() sanitises them.
97-
param_map = {_cypher_param_name(k): v for k, v in properties.items()}
98-
where_clauses = " AND ".join(f"n.{_cypher_ident(k)} = ${_cypher_param_name(k)}" for k in properties)
94+
# Use indexed parameter names (p0, p1, …) to avoid collisions when
95+
# different property keys sanitize to the same Cypher identifier
96+
# (e.g. "a-b" and "a_b" both become "a_b").
97+
param_map: dict[str, Any] = {}
98+
clauses: list[str] = []
99+
for idx, (k, v) in enumerate(properties.items()):
100+
pname = f"p{idx}"
101+
clauses.append(f"n.{_cypher_ident(k)} = ${pname}")
102+
param_map[pname] = v
103+
where_clauses = " AND ".join(clauses)
99104
cypher = f"MATCH (n) WHERE {where_clauses} RETURN n, n.id AS _nid LIMIT 1000"
100105
result = self._client.cypher(cypher, params=param_map)
101106
for row in result:

tests/integration/test_sdk.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,7 @@ def test_hybrid_search_returns_results(client):
226226
vec = [float(i) / 8 for i in range(8)]
227227
# Create two nodes connected by RELATED edge, both have embeddings.
228228
client.cypher(
229-
"CREATE (a:HybridTest {tag: $tag, embedding: $vec})"
230-
"-[:RELATED]->"
231-
"(b:HybridTest {tag: $tag, embedding: $vec})",
229+
"CREATE (a:HybridTest {tag: $tag, embedding: $vec})-[:RELATED]->(b:HybridTest {tag: $tag, embedding: $vec})",
232230
params={"tag": tag, "vec": vec},
233231
)
234232
# Retrieve the start node id.
@@ -247,7 +245,7 @@ def test_hybrid_search_returns_results(client):
247245
top_k=1,
248246
vector_property="embedding",
249247
)
250-
assert len(results) >= 1, f"hybrid_search returned no results"
248+
assert len(results) >= 1, "hybrid_search returned no results"
251249
assert hasattr(results[0], "distance")
252250
assert hasattr(results[0], "node")
253251
finally:

0 commit comments

Comments
 (0)