fix(retriever): search a graph source classically when its graph answers nothing

Only a raise routed a source to the ClassicRAG fallback, and every graph read
logs its own failure and returns empty. So a query that broke, a half-built
graph and a walk that genuinely found nothing were indistinguishable, and each
made the source contribute nothing at all to the answer -- no fallback, no
vector blend, which is skipped by the same early return.

A graph source that produces no documents now joins the classic batch, exactly
as a source with no graph already does.

The passage stage also rescanned every node's chunk list once per candidate.
It inverts the links once instead: 7.3 ms to 0.16 ms on a 400-node subgraph at
the candidate cap, with identical output.
This commit is contained in:
Alex committed 2026-09-20 10:45:03 +01:00
1 parent 8c5a5190d9
commit bc0ef9f3b0
3 files changed
+76 -16

No files matched your search

+19
View File
@@ -130,3 +130,22 @@ class TestChunkSimilaritiesGuard:
store = object.__new__(GraphStore)
assert store.chunk_similarities("src", chunk_ids, embedding) == {}
@pytest.mark.unit
class TestNodesByChunk:
"""The passage stage inverts node->chunks once instead of rescanning."""
def test_inverts_and_keeps_node_order(self):
from docsgpt.retriever.graph_rag import _nodes_by_chunk
assert _nodes_by_chunk({"n1": ["c1", "c2"], "n2": ["c2"], "n3": []}) == {
"c1": ["n1"],
"c2": ["n1", "n2"],
}
def test_no_links_invert_to_nothing(self):
from docsgpt.retriever.graph_rag import _nodes_by_chunk
assert _nodes_by_chunk({}) == {}
assert _nodes_by_chunk({"n1": None}) == {}