Skip to content

boost exact-match seeds and stop hubs hijacking query output - #638

Closed
ReverendJack3000 wants to merge 1 commit into
Graphify-Labs:v5from
ReverendJack3000:exact-match-boost
Closed

boost exact-match seeds and stop hubs hijacking query output#638
ReverendJack3000 wants to merge 1 commit into
Graphify-Labs:v5from
ReverendJack3000:exact-match-boost

Conversation

@ReverendJack3000

Copy link
Copy Markdown

What

_score_nodes gets an EXACT_MATCH_BONUS for terms that equal a node's norm_label (or norm_label with trailing () stripped, since the AST extractor emits function labels as foo()), and _subgraph_to_text accepts an optional seeds arg so the rendered output emits seeds first in score order before the BFS expansion sorted by degree desc.

Why

Single-token identifier queries — by far the most common usage — get buried under high-degree hub modules. Two issues compound:

  1. _score_nodes only used substring matching, so the seed function and every sibling that mentioned the symbol tied at score 1; ties broke alphabetically on node id.
  2. _subgraph_to_text always re-sorted the rendered nodes by G.degree(n) descending, so even when _score_nodes returned the right seed, the hub it expanded into (e.g. app.js, controller.js) always rendered first.

Before

$ graphify query "pasteFromClipboard" --budget 600
NODE app.js [src=frontend/app.js loc=L1 community=3]
NODE now() [src=frontend/modules/core/perf.js loc=L1 community=1]
NODE .filter() [src=backend/tests/_fake_inventory.py loc=L80 community=6]
NODE draw() [src=frontend/app.js loc=L4047 community=3]
NODE FakeInventoryQuery [src=backend/tests/_fake_inventory.py loc=L18 community=1]
...

pasteFromClipboard() does not appear in the first 30 NODE lines on a ~6k-node graph.

After

$ graphify query "pasteFromClipboard" --budget 600
NODE pasteFromClipboard() [src=frontend/app.js loc=L4847 community=3]
NODE app.js [src=frontend/app.js loc=L1 community=3]
NODE now() [src=frontend/modules/core/perf.js loc=L1 community=1]
...

Scope of behavior change

  • seeds=None in _subgraph_to_text preserves the previous degree-desc ordering exactly. The only call sites updated to pass seeds= are _tool_query_graph (MCP) and the query CLI handler in __main__.py. _tool_shortest_path and the path CLI use _score_nodes only for endpoint resolution and do not render via _subgraph_to_text, so they automatically benefit from the bonus without further changes.
  • Multi-term / prose queries are unaffected because the bonus only fires on term == norm_label exactly. Substring scoring is untouched.
  • EXACT_MATCH_BONUS = 100.0 swamps any plausible substring sum (substring score caps at ~1.5/term).

Tests

Six new cases in tests/test_serve.py:

  • test_score_nodes_exact_match_beats_substring — exact match outranks substring distractors on the same graph.
  • test_score_nodes_exact_match_strips_function_parenssaveDiagram() matches term savediagram.
  • test_score_nodes_exact_match_no_false_positive — unrelated term still returns no matches.
  • test_subgraph_to_text_seeds_render_first — seed before hub when seeds=[...].
  • test_subgraph_to_text_no_seeds_preserves_legacy_orderseeds=None matches no-arg call (back-compat guard) and hub still wins on degree.
  • test_query_pipeline_exact_match_ranks_above_hub — end-to-end _score_nodes -> _bfs -> _subgraph_to_text(seeds=...).

Full suite (447 tests) green.

Single-token queries against an identifier (e.g. `graphify query
"pasteFromClipboard"`) used to surface high-degree hub modules like
`app.js` first because two issues compound:

1. _score_nodes only used substring matching, so the function and every
   sibling that mentioned it tied at score 1; ties broke alphabetically
   on node id.
2. _subgraph_to_text always re-sorted the rendered nodes by
   G.degree(n) desc, so even when the right seed reached the BFS, the
   hub it expanded into rendered first.

Fix:

- Add EXACT_MATCH_BONUS in _score_nodes when a query term equals
  norm_label or norm_label without trailing parens (the AST extractor
  emits function labels as `foo()`).
- Give _subgraph_to_text an optional `seeds` arg; seeds render first in
  the supplied order, the rest fall back to degree desc. seeds=None
  matches the legacy ordering, so other call sites are untouched.
- Plumb seeds through _tool_query_graph (MCP) and the `query` CLI
  handler.

Six new tests cover the bonus, the trailing-parens strip, the seed
renders-first invariant, the legacy back-compat path, and the full
score -> bfs -> render pipeline. Full suite (447 tests) green.
safishamsi added a commit that referenced this pull request May 1, 2026
…ne comments, query boost, cache race, markdownify, content hash

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@safishamsi

Copy link
Copy Markdown
Collaborator

The requested changes have been implemented in 3fdae8f. Added _EXACT_MATCH_BONUS = 100.0 to _score_nodes for exact label matches (strips trailing () for functions), and seeds now render first in _subgraph_to_text before the degree-sorted expansion. Shipped in v0.6.2.

@safishamsi safishamsi closed this May 1, 2026
matzls pushed a commit to matzls/graphify that referenced this pull request May 10, 2026
…bs#638 Graphify-Labs#589 Graphify-Labs#586 Graphify-Labs#593: kimi thinking, manifest, inline comments, query boost, cache race, markdownify, content hash

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants