feat: refocus on drawings — code/ADA gated off, drawing-integrity wave, Brain-directed clarification
Docker Release / build-and-push (push) Successful in 1m8s
Docker Release / release (push) Skipped

- ENABLE_CODE_REVIEW flag (default off): skips code/ADA/jurisdiction review
  path in both pipelines; nothing deleted, one env flag to restore.
- Per-sheet Drawing Integrity QA wave (agent + classic, default on):
  dangling refs, on-sheet contradictions, dimension sanity, missing sheet
  essentials, tag hygiene. New DrawingIntegrityAgent + classic stage.
- Broadened conflict critic: intra-sheet + same-discipline contradictions,
  not just cross-discipline.
- Wave 6.5 Brain-directed clarification (bounded hub-and-spoke): Brain names
  uncertain findings, verify_evidence requests route through the wave-5b
  verifier; refuted findings suppressed. One planning call + capped verifies,
  single iteration. Shared _build_verify_scopes across 5b and 6.5.
- Config knobs, .env.example, frontend copy, tests (182 passing).
This commit is contained in:
2026-08-20 15:10:32 -05:00
parent bae608a505
commit d37ac8c1c7
22 changed files with 1917 additions and 63 deletions
+88
View File
@@ -0,0 +1,88 @@
"""
drawing_integrity.py - Per-sheet Drawing Integrity QA (LLM, classic pipeline).
The drawing-focused pass: reads ONE sheet's own extracted objects + sheet image
+ deterministic text layer and flags defects internal to that single sheet
(dangling detail/callout/keynote references, schedule-vs-plan/legend
disagreements on the same sheet, dimension strings that do not sum, missing
title-block/scale essentials, duplicate/inconsistent tags). It complements the
cross-sheet conflict checker; it never does code/ADA or cross-sheet
coordination. Emits the canonical issue schema. Returns [] on failure.
Gated by config.ENABLE_DRAWING_INTEGRITY. Runs sheets concurrently, one call
per sheet, skipping sheets below INTEGRITY_MIN_ASSERTIONS.
"""
from concurrent.futures import ThreadPoolExecutor
from typing import Dict, List
from backend import config
from backend.agents.prompts import (
DRAWING_INTEGRITY_SYSTEM_PROMPT,
DRAWING_INTEGRITY_USER_PROMPT,
)
from backend.pipeline._serialize import dumps
from backend.pipeline._stage import call_stage, collect_list, validate_issue
def _sheet_meta(sheet: Dict) -> Dict:
return {
"sheet_number": sheet.get("sheet_number"),
"sheet_title": sheet.get("sheet_title"),
"discipline": sheet.get("discipline"),
"drawing_type": sheet.get("drawing_type"),
"level": sheet.get("level"),
"scale": sheet.get("scale"),
}
def _review_sheet(sheet: Dict, page_to_b64: Dict, page_to_text: Dict) -> List[Dict]:
page_number = sheet.get("page_number")
assertions = (sheet.get("assertions") or [])[
:config.AGENT_INTEGRITY_MAX_ASSERTIONS
]
text_layer = (page_to_text.get(page_number) or "")[
:config.TEXT_LAYER_MAX_CHARS
]
image = page_to_b64.get(page_number)
images = [image][:config.AGENT_INTEGRITY_MAX_IMAGES] if image else []
parsed = call_stage(
DRAWING_INTEGRITY_SYSTEM_PROMPT,
DRAWING_INTEGRITY_USER_PROMPT,
subs={
"sheet_meta": dumps(_sheet_meta(sheet)),
"assertions": dumps(assertions),
"text_layer": text_layer,
},
images_b64=images,
max_tokens=config.INTEGRITY_MAX_TOKENS,
)
issues = collect_list(
parsed, "issues", lambda c: validate_issue(c, "drawing_integrity")
)
sheet_number = sheet.get("sheet_number")
for issue in issues:
if not issue.get("sheets") and sheet_number:
issue["sheets"] = [sheet_number]
return issues
def drawing_integrity_review(sheets: List[Dict], pages: List[Dict]) -> List[Dict]:
"""One LLM call per non-sparse sheet, run concurrently."""
if not config.ENABLE_DRAWING_INTEGRITY:
return []
page_to_b64 = {p["page_number"]: p.get("base64") for p in pages}
page_to_text = {p["page_number"]: p.get("text_layer") for p in pages}
targets = [
s for s in sheets
if len(s.get("assertions") or []) >= config.INTEGRITY_MIN_ASSERTIONS
]
issues: List[Dict] = []
if targets:
with ThreadPoolExecutor(max_workers=config.AGENT_INTEGRITY_CONCURRENCY) as pool:
for res in pool.map(
lambda s: _review_sheet(s, page_to_b64, page_to_text), targets
):
issues.extend(res)
print(f"[DrawingIntegrity] {len(issues)} issue(s) across {len(targets)} sheet(s)")
return issues