feat(graphrag): durable extract_graph task + ingest-path enqueue + enable route

graph_enabled() sets kind=graphrag + retriever=graphrag. extract_graph_worker fetches the source's pgvector chunks and runs G3 extraction (graphrag_available guard, empty no-op). extract_graph durable+idempotent task; key varies with source updated_at so re-ingest/re-enable re-run incrementally (G3 checkpoint skips done chunks) while concurrent same-state enqueues dedup. The 4 ingest paths enqueue after embed when kind=graphrag (isolated in try/except so a broker hiccup can't fail the ingest). POST /api/sources/<id>/graphrag/enable: pgvector+GRAPHRAG_ENABLED gated, owner/editor write-authz; PATCH config still can't flip kind->graphrag. Unit G4.
This commit is contained in:
Alex committed 2026-06-23 01:29:51 +01:00
1 parent 22582e900c
commit 4742aec4c6
9 files changed
+844

No files matched your search

+38
View File
@@ -100,6 +100,43 @@ class TestConvertSourceToWikiTask:
assert result == {"status": "converted"}
class TestExtractGraphTask:
@pytest.mark.unit
@patch("application.worker.extract_graph_worker")
def test_calls_extract_graph_worker(self, mock_worker):
from application.api.user.tasks import extract_graph
mock_worker.return_value = {"nodes": 2, "edges": 1}
result = extract_graph("source123", "user1")
mock_worker.assert_called_once_with(ANY, "source123", "user1")
assert result == {"nodes": 2, "edges": 1}
@pytest.mark.unit
def test_repeat_with_same_key_short_circuits(self, pg_conn):
from application.api.user import tasks
calls: list[str] = []
def _fake_worker(self, source_id, user):
calls.append(source_id)
return {"nodes": 1, "edges": 0}
with _patch_decorator_db(pg_conn), patch(
"application.worker.extract_graph_worker", _fake_worker
):
first = tasks.extract_graph(
"src-g", "user1", idempotency_key="extract-graph:src-g",
)
second = tasks.extract_graph(
"src-g", "user1", idempotency_key="extract-graph:src-g",
)
assert first == second
assert len(calls) == 1
class TestScheduleSyncsTask:
@pytest.mark.unit
@patch("application.api.user.tasks.sync_worker")
@@ -302,6 +339,7 @@ class TestDurableTaskRetryPolicy:
"ingest_connector_task",
"reembed_wiki_page",
"convert_source_to_wiki",
"extract_graph",
],
)
def test_task_has_retry_config(self, task_name):