Skip to content

Commit 020cca2

Browse files
candy-TongOmX
andauthored
Keep non-English query terms searchable (#964)
Graph queries filtered every token with len > 2, which dropped common two-character Chinese search terms while trying to suppress short English noise. Centralize query token selection and apply the length gate only to pure-English tokens so mixed or non-English terms remain searchable. Constraint: Issue #962 reports space-separated Chinese query terms such as 前端, 依赖, and 安装 are lost by graphify query. Rejected: Add Chinese segmentation now | the reported failure is fixed by preserving existing space-separated non-English tokens without expanding query behavior. Confidence: high Scope-risk: narrow Directive: Keep CLI, MCP query, and benchmark query tokenization on one helper when changing query-term rules. Tested: uv run --with pytest pytest tests/test_serve.py tests/test_query_cli.py tests/test_benchmark.py Tested: graphify update . Not-tested: Full test suite. Co-authored-by: OmX <omx@oh-my-codex.dev>
1 parent 406bea4 commit 020cca2

4 files changed

Lines changed: 37 additions & 2 deletions

File tree

graphify/benchmark.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from networkx.readwrite import json_graph
88

99
from graphify.build import edge_data
10+
from graphify.serve import _query_terms
1011

1112

1213
_CHARS_PER_TOKEN = 4 # standard approximation
@@ -37,7 +38,7 @@ def _estimate_tokens(text: str) -> int:
3738

3839
def _query_subgraph_tokens(G: nx.Graph, question: str, depth: int = 3) -> int:
3940
"""Run BFS from best-matching nodes and return estimated tokens in the subgraph context."""
40-
terms = [t.lower() for t in question.split() if len(t) > 2]
41+
terms = _query_terms(question)
4142
scored = []
4243
for nid, data in G.nodes(data=True):
4344
label = data.get("label", "").lower()

graphify/serve.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ def _strip_diacritics(text: str) -> str:
5050
return "".join(c for c in nfkd if not unicodedata.combining(c))
5151

5252

53+
def _query_terms(question: str) -> list[str]:
54+
"""Split a query into searchable terms, filtering only short English terms."""
55+
terms: list[str] = []
56+
for raw in question.split():
57+
term = raw.lower().strip()
58+
if not term:
59+
continue
60+
is_english_only = all("a" <= ch <= "z" for ch in term)
61+
if not is_english_only or len(term) > 2:
62+
terms.append(term)
63+
return terms
64+
65+
5366
_EXACT_MATCH_BONUS = 1000.0
5467
_PREFIX_MATCH_BONUS = 100.0
5568
_SUBSTRING_MATCH_BONUS = 1.0
@@ -306,7 +319,7 @@ def _query_graph_text(
306319
token_budget: int = 2000,
307320
context_filters: list[str] | None = None,
308321
) -> str:
309-
terms = [t.lower() for t in question.split() if len(t) > 2]
322+
terms = _query_terms(question)
310323
scored = _score_nodes(G, terms)
311324
start_nodes = _pick_seeds(scored)
312325
if not start_nodes:

tests/test_benchmark.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ def test_query_bfs_expands_neighbors():
4747
assert tokens_deep >= tokens_shallow
4848

4949

50+
def test_query_keeps_short_non_english_terms():
51+
G = nx.Graph()
52+
G.add_node("frontend", label="前端", source_file="docs/前端.md", source_location="L1", community=0)
53+
tokens = _query_subgraph_tokens(G, "前端", depth=1)
54+
assert tokens > 0
55+
56+
5057
# --- run_benchmark ---
5158

5259
def test_run_benchmark_returns_reduction(tmp_path):

tests/test_serve.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
_dfs,
1414
_filter_graph_by_context,
1515
_infer_context_filters,
16+
_query_terms,
1617
_query_graph_text,
1718
_resolve_context_filters,
1819
_subgraph_to_text,
@@ -79,6 +80,19 @@ def test_score_nodes_source_file_partial():
7980
assert "n2" in nids
8081

8182

83+
def test_query_terms_filters_only_short_english_terms():
84+
terms = _query_terms("前端 dependency 依赖 install 安装 to of 包管理器 项目约定 a前")
85+
assert terms == ["前端", "dependency", "依赖", "install", "安装", "包管理器", "项目约定", "a前"]
86+
87+
88+
def test_query_graph_text_keeps_short_non_english_terms():
89+
G = nx.Graph()
90+
G.add_node("frontend", label="前端", source_file="docs/前端.md", source_location="L1", community=0)
91+
text = _query_graph_text(G, "前端", mode="bfs", depth=1)
92+
assert "No matching nodes found." not in text
93+
assert "NODE 前端" in text
94+
95+
8296
def test_infer_context_filters_for_calls_question():
8397
assert _infer_context_filters("who calls extract") == ["call"]
8498

0 commit comments

Comments
 (0)