Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions lifecycle/graph_miners.py
Original file line number Diff line number Diff line change
Expand Up @@ -1283,16 +1283,17 @@ async def miner_zero_results(cm: AsyncConnectionManager, layer: str) -> dict[str
if not rows:
return {"edges": 0}
surfaced = 0
try:
from graph.epistemic import EpistemicGraph

g = EpistemicGraph(cm=cm, layer=layer)
for r in rows:
await g.find_or_add_entity(str(r["user_id"]), str(r["query"])[:300], "question")
surfaced += 1
except Exception:
logger.debug("zero-result surfacing failed", exc_info=True)
return {"edges": 0}
# No try/except here, for the same reason `miner_embedding` has none: a
# failure that returns `{"edges": 0}` is indistinguishable from "there was
# nothing to surface", and this one logged at `debug`, which is invisible
# at the level this service actually runs at. Letting it escape reaches the
# handler in `graph_enrich` that records `edges: -1` with a warning.
from graph.epistemic import EpistemicGraph

g = EpistemicGraph(cm=cm, layer=layer)
for r in rows:
await g.find_or_add_entity(str(r["user_id"]), str(r["query"])[:300], "question")
surfaced += 1
return {"edges": surfaced}


Expand Down
29 changes: 29 additions & 0 deletions tests/test_s17_stage1_tail.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,35 @@ async def test_zero_result_single_hit_not_surfaced(migrated_cm) -> None:
assert res["edges"] == 0, "один провал — не сигнал"


@pytest.mark.asyncio
async def test_zero_result_miner_lets_a_failure_escape(migrated_cm, monkeypatch) -> None:
"""Сбой обязан выйти наружу, а не стать нулём с записью в `debug`.

07.10.2026: здесь был второй такой же глушитель, как в `miner_embedding` —
`except Exception` писал в `debug` (невидимо на рабочем уровне) и возвращал
`{"edges": 0}`, то есть «нечего выносить». Договор один на всех шахтёров:
ноль — это работа, которая не нашлась, а сбой пусть ловит `graph_enrich`.
"""
from graph.epistemic import EpistemicGraph
from lifecycle.graph_miners import log_zero_result, miner_zero_results
from shared.constants import DB_NAME

cm = migrated_cm
conn = await cm.get(DB_NAME)
await conn.execute("DELETE FROM recall_zero_results")
await conn.commit()
for _ in range(2):
await log_zero_result(cm, "user", "s17zr3", "провал запроса, который надо вынести наверх")

async def _boom(self, *args, **kwargs):
raise RuntimeError("find_or_add_entity is down")

monkeypatch.setattr(EpistemicGraph, "find_or_add_entity", _boom)

with pytest.raises(RuntimeError, match="find_or_add_entity is down"):
await miner_zero_results(cm, "user")


# ── 7. counter-signal алиасы ─────────────────────────────────────────────────


Expand Down
Loading