87 lines
3.7 KiB
Python
87 lines
3.7 KiB
Python
"""Runner-level wave-5b tests: suppression path and zero-image guard."""
|
|
|
|
import backend.agents.runner as runner_mod
|
|
from backend.agents.base import AgentResult
|
|
from backend.agents.runner import run_agent_pipeline
|
|
|
|
|
|
def _finding(sheets):
|
|
return {
|
|
"issue_id": "C1", "severity": "critical", "confidence": "high",
|
|
"source_stage": "constructability", "sheets": sheets,
|
|
"description": "HSS16x4 on (2) 2x6 STUD PACK is unbuildable",
|
|
"evidence": [{"sheet": sheets[0], "source_text": "(2) 2x6 STUD PACK"}],
|
|
}
|
|
|
|
|
|
def _stub_agent(artifacts):
|
|
return lambda usage: type("S", (), {
|
|
"name": "stub",
|
|
"run": lambda self, scope: AgentResult(
|
|
scope_id=scope.scope_id, artifacts=list(artifacts)),
|
|
})()
|
|
|
|
|
|
def _patch_pipeline(monkeypatch, finding):
|
|
monkeypatch.setattr(
|
|
runner_mod, "convert_pdf_to_images",
|
|
lambda path: [{"page_number": 1, "base64": "QUJD"}])
|
|
monkeypatch.setattr(runner_mod, "SheetExtractorAgent", _stub_agent([
|
|
{"sheet_number": "S401", "page_number": 1, "level": "roof",
|
|
"discipline": "S", "assertions": [
|
|
{"text": "(2) 2x6 STUD PACK", "object_type": "framing"},
|
|
{"text": "HSS16X4 beam", "object_type": "framing"},
|
|
]},
|
|
]))
|
|
monkeypatch.setattr(runner_mod, "SheetIndexAgent", _stub_agent([{}]))
|
|
monkeypatch.setattr(runner_mod, "JurisdictionAgent", _stub_agent([{}]))
|
|
monkeypatch.setattr(runner_mod, "LinkerAgent", _stub_agent([
|
|
{"key": "c1", "location": "roof beam pocket", "assertions": []},
|
|
]))
|
|
monkeypatch.setattr(runner_mod, "ConflictCriticAgent", _stub_agent([]))
|
|
monkeypatch.setattr(runner_mod, "CodeAgent", _stub_agent([]))
|
|
monkeypatch.setattr(runner_mod, "ConstructabilityAgent", _stub_agent([finding]))
|
|
monkeypatch.setattr(runner_mod, "CompletenessAgent", _stub_agent([]))
|
|
monkeypatch.setattr(
|
|
runner_mod, "BrainAgent",
|
|
lambda usage: type("B", (), {
|
|
"run": lambda self, findings, sheet_index, jurisdiction:
|
|
(list(findings), [])})())
|
|
|
|
|
|
def test_refuted_finding_is_suppressed_not_crash(monkeypatch, tmp_path):
|
|
"""Regression: memory.replace("suppressed", ...) must not KeyError."""
|
|
_patch_pipeline(monkeypatch, _finding(["S401"]))
|
|
monkeypatch.setattr(
|
|
"backend.agents.verifier.call_json",
|
|
lambda **kwargs: {"verdicts": [
|
|
{"sheet": "S401", "source_text": "(2) 2x6 STUD PACK",
|
|
"verdict": "corrected", "actual_text": "(5) 2x6 STUD PACK",
|
|
"notes": "callout reads (5)"},
|
|
]})
|
|
pdf = tmp_path / "dummy.pdf"
|
|
pdf.write_bytes(b"%PDF-1.4\n")
|
|
report = run_agent_pipeline(str(pdf), out_dir=str(tmp_path),
|
|
require_review=False)
|
|
assert [f["issue_id"] for f in report["suppressed_issues"]] == ["C1"]
|
|
assert report["suppressed_issues"][0]["verification"]["status"] == "refuted"
|
|
|
|
|
|
def test_zero_image_finding_is_not_suppressed(monkeypatch, tmp_path):
|
|
"""A finding whose sheets resolve to no page images must not be judged
|
|
(and must never be refuted) without pixels."""
|
|
_patch_pipeline(monkeypatch, _finding(["S999"])) # no such sheet
|
|
monkeypatch.setattr(
|
|
"backend.agents.verifier.call_json",
|
|
lambda **kwargs: {"verdicts": [
|
|
{"sheet": "S999", "source_text": "(2) 2x6 STUD PACK",
|
|
"verdict": "not_found", "actual_text": None, "notes": None},
|
|
]})
|
|
pdf = tmp_path / "dummy.pdf"
|
|
pdf.write_bytes(b"%PDF-1.4\n")
|
|
report = run_agent_pipeline(str(pdf), out_dir=str(tmp_path),
|
|
require_review=False)
|
|
assert report["suppressed_issues"] == []
|
|
validated = report.get("validated_issues") or []
|
|
assert any(f.get("issue_id") == "C1" for f in validated)
|