fix(graphrag): review fixes — replay-safe writes, strict count, zero weights

Three findings from review, all on code this branch introduced.

apply_chunk was not replay-safe. commit() can report connection loss *after*
Postgres committed, and the reconnect retry then replays the write: _upsert_node
bumps doc_freq a second time and _add_edge inserts another row, since
graph_edges has no uniqueness constraint for a logical edge. The chunk's
graph_ingest_progress row is now written in the same transaction as the rows it
describes, and a replay that finds it already "done" returns (0, 0) without
touching the graph. Extraction drops its separate mark_chunk("done"): the
checkpoint and the graph can no longer disagree.

count_nodes swallows every query failure and answers 0, so extraction's
"fall back to the write count" handler could never run — a failed count after a
successful build reported an empty graph. count_nodes grows a strict mode that
re-raises; retrieval keeps the swallow, which is what routes a source to
ClassicRAG.

A zero edge weight was read as a full-strength link: `or 1.0` rewrote an
explicit 0 before the <= 0 filter. Only missing and null weights default now.
The same coercion sat in _ppr_scores, where it would have kept the ranker's
rule unreachable from the product path, so it is fixed there too.
This commit is contained in:
Alex committed 2026-09-17 16:31:19 +01:00
1 parent 9e9f130ed0
commit 3f774d813c
6 files changed
+274 -9

No files matched your search

+36
View File
@@ -624,6 +624,42 @@ class TestSummaryNodeCount:
store.delete_by_source(source_id)
@pytest.mark.unit
class TestSummaryCountFailure:
"""A broken count query must not be reported as an empty graph."""
def test_a_failed_count_reports_the_write_count(
self, monkeypatch, stub_embedding
):
from unittest.mock import MagicMock
store = MagicMock(name="GraphStore")
store.pending_chunks.return_value = ["c1"]
store.apply_chunk.return_value = (2, 1)
store.count_nodes.side_effect = RuntimeError("count query failed")
monkeypatch.setattr(
"docsgpt.graphrag.store.GraphStore", lambda *a, **k: store
)
_install_stub_llm(
monkeypatch,
_StubLLM([_extraction_json([{"name": "Ada"}], [])]),
)
summary = extract_graph_for_source(
str(uuid.uuid4()),
user="owner-1",
chunks=[_chunk("c1", "Ada.")],
config=SourceConfig(),
request_id="req-1",
)
# Falls back to what was actually written, not to zero.
assert summary["nodes"] == 2
# And it asked for a count that raises rather than one that returns 0,
# or the fallback above could never run.
assert store.count_nodes.call_args.kwargs.get("strict") is True
@pytest.mark.unit
class TestParsing:
def test_parses_embedded_json(self):