87 lines
4.4 KiB
Python
87 lines
4.4 KiB
Python
from backend import config
|
|
from backend.agents.base import AgentScope, AgentUsage
|
|
from backend.agents.extractors import SheetExtractorAgent
|
|
from backend.prompts import TEXT_STRUCTURING_SYSTEM_PROMPT, TEXT_STRUCTURING_USER_INSTRUCTION
|
|
|
|
def test_text_structuring_prompt_demands_verbatim_and_completeness():
|
|
assert "verbatim" in TEXT_STRUCTURING_USER_INSTRUCTION.lower()
|
|
assert "every" in TEXT_STRUCTURING_USER_INSTRUCTION.lower()
|
|
assert "{text_layer}" in TEXT_STRUCTURING_USER_INSTRUCTION
|
|
|
|
|
|
def _page(n=8, text="1. \nALL SAWN LUMBER IN CONTACT WITH SOIL TO BE SOUTHERN PINE, PRESSURE TREATED.\n2. \nROOF SHEATHING: 5/8\" PLYWOOD, C-D GRADE, STRUCTURAL I."):
|
|
return {"page_number": n, "base64": "AAAA", "text_layer": text}
|
|
|
|
|
|
def _run(agent, page, hint=""):
|
|
scope = AgentScope(scope_id=f"sheet:{page['page_number']}",
|
|
payload={"page": page, "sheet_hint": hint})
|
|
result = agent.run(scope)
|
|
assert not result.error, result.error
|
|
return result.artifacts[0]
|
|
|
|
|
|
def test_ladder_falls_back_when_vision_returns_nothing(monkeypatch):
|
|
# vision pass returns 1 summary object that the guard drops;
|
|
# text-structuring disabled to exercise the deterministic rung
|
|
monkeypatch.setattr("backend.agents.extractors.call_json",
|
|
lambda **kw: [{"name": "general notes", "value": "notes"}])
|
|
monkeypatch.setattr("backend.config.EXTRACT_TEXT_RETRY_ENABLED", False)
|
|
agent = SheetExtractorAgent(AgentUsage())
|
|
sheet = _run(agent, _page())
|
|
assert sheet["assertions"], "dark sheet must be impossible with fallback enabled"
|
|
assert all(a.get("grounding") == "text_layer_fallback" for a in sheet["assertions"])
|
|
assert sheet["coverage"]["ratio"] >= 0.6
|
|
|
|
|
|
def test_ladder_merge_preserves_graphical_objects(monkeypatch):
|
|
# vision finds a graphical symbol; text rung adds notes.
|
|
# The graphical object MUST survive the merge.
|
|
calls = {"n": 0}
|
|
def fake_call_json(**kw):
|
|
calls["n"] += 1
|
|
if kw.get("images_b64"): # vision pass
|
|
return {"sheet": {}, "objects": [
|
|
{"object_id": "g1", "object_type": "lighting_fixture",
|
|
"name": "pendant at grid C-4", "source_text": None,
|
|
"graphical_basis": "16in pendant symbol at grid C-4"}]}
|
|
return {"sheet": {}, "objects": [ # text-structuring pass
|
|
{"object_id": "t1", "object_type": "general_note",
|
|
"source_text": "ALL SAWN LUMBER IN CONTACT WITH SOIL TO BE SOUTHERN PINE, PRESSURE TREATED.",
|
|
"name": "lumber note"}]}
|
|
monkeypatch.setattr("backend.agents.extractors.call_json", fake_call_json)
|
|
agent = SheetExtractorAgent(AgentUsage())
|
|
sheet = _run(agent, _page())
|
|
assert calls["n"] >= 2, "text-structuring rung should have fired"
|
|
assert any(a.get("graphical_basis") for a in sheet["assertions"])
|
|
assert any("SAWN LUMBER" in (a.get("source_text") or "") for a in sheet["assertions"])
|
|
|
|
|
|
def test_ladder_recovers_sheet_number_from_text_layer(monkeypatch):
|
|
monkeypatch.setattr(
|
|
"backend.agents.extractors.call_json",
|
|
lambda **kw: {"sheet": {}, "objects": [
|
|
{"object_id": "o1", "name": "RCP note",
|
|
"source_text": "GYP. BD. CEILING 8'-11 3/8\" A.F.F. TYP. FOR ALL STOREFRONT",
|
|
"attributes": {"height": "8'-11 3/8\""}}]})
|
|
agent = SheetExtractorAgent(AgentUsage())
|
|
sheet = _run(agent, _page(18, "REFLECTED CEILING PLAN\nGYP. BD. CEILING 8'-11 3/8\" A.F.F. TYP. FOR ALL STOREFRONT\nA102"))
|
|
assert sheet["sheet_number"] == "A102"
|
|
|
|
|
|
def test_ladder_skips_retry_when_coverage_healthy(monkeypatch):
|
|
# vision covers every meaningful text-layer line -> no rung 2/3 calls
|
|
calls = {"n": 0}
|
|
def fake_call_json(**kw):
|
|
calls["n"] += 1
|
|
return {"sheet": {"sheet_number": "A101"}, "objects": [
|
|
{"object_id": "o1", "object_type": "general_note", "name": "lumber note",
|
|
"source_text": "ALL SAWN LUMBER IN CONTACT WITH SOIL TO BE SOUTHERN PINE, PRESSURE TREATED."},
|
|
{"object_id": "o2", "object_type": "general_note", "name": "sheathing note",
|
|
"source_text": "ROOF SHEATHING: 5/8\" PLYWOOD, C-D GRADE, STRUCTURAL I."}]}
|
|
monkeypatch.setattr("backend.agents.extractors.call_json", fake_call_json)
|
|
agent = SheetExtractorAgent(AgentUsage())
|
|
sheet = _run(agent, _page())
|
|
assert sheet["coverage"]["ratio"] >= config.EXTRACT_COVERAGE_FLOOR
|
|
assert calls["n"] == 1
|