Agent web jobs now stop after Brain consolidation and enter needs_review with a persisted review queue (blocking: high-severity, low-confidence, sensitive-category findings; audit sample of clean clusters). Humans decide confirm/reject/unsure/needs_clarification via new review API and frontend queue; a finalizer applies decisions (rejections suppressed with reason codes), performs bounded targeted reruns for clarifications, drafts RFIs only for kept issues, and only then marks the job done and sends the final email. Two-phase email (review-required, then final report), per-decision feedback labels with redacted aggregate metrics, restart recovery from job artifacts, and CLI --no-review bypass. Classic pipeline unchanged. 65 non-LLM tests.
86 lines
3.1 KiB
Python
86 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
run_check.py - CLI entry point for the conflict checker (the tuning loop).
|
|
|
|
Usage:
|
|
python cli/run_check.py path/to/set.pdf [--out out_dir]
|
|
|
|
Run from the project root so `backend` is importable.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import argparse
|
|
|
|
# Make the project root importable when run as a script.
|
|
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
if _ROOT not in sys.path:
|
|
sys.path.insert(0, _ROOT)
|
|
|
|
from backend import config # noqa: E402
|
|
from backend.agents.runner import run_agent_pipeline # noqa: E402
|
|
from backend.pipeline.runner import run_pipeline # noqa: E402
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Cross-discipline drawing conflict checker")
|
|
parser.add_argument("pdf", help="Path to the PDF drawing set")
|
|
parser.add_argument("--out", default=None,
|
|
help="Directory for artifacts (default: out/<pdf-stem>)")
|
|
parser.add_argument("--mode", choices=("classic", "agent"), default="classic",
|
|
help="Pipeline implementation to run (default: classic)")
|
|
parser.add_argument("--project-name", default=None)
|
|
parser.add_argument("--address", default=None)
|
|
parser.add_argument("--occupancy", default=None)
|
|
parser.add_argument("--work-type", default=None,
|
|
help="new_building | remodel | tenant_improvement | addition | ...")
|
|
parser.add_argument("--no-review", action="store_true",
|
|
help="Agent mode only: skip the human-review gate and finish the run "
|
|
"(overrides AGENT_REQUIRE_REVIEW=true)")
|
|
args = parser.parse_args()
|
|
|
|
if not os.path.isfile(args.pdf):
|
|
print(f"Error: file not found: {args.pdf}")
|
|
return 1
|
|
|
|
project_input = {
|
|
k: v for k, v in {
|
|
"project_name": args.project_name, "address": args.address,
|
|
"occupancy": args.occupancy, "work_type": args.work_type,
|
|
}.items() if v
|
|
}
|
|
out_dir = args.out or os.path.join("out", os.path.splitext(os.path.basename(args.pdf))[0])
|
|
if args.mode == "agent":
|
|
report = run_agent_pipeline(
|
|
args.pdf,
|
|
out_dir=out_dir,
|
|
project_input=project_input or None,
|
|
source_name=os.path.basename(args.pdf),
|
|
require_review=config.AGENT_REQUIRE_REVIEW and not args.no_review,
|
|
)
|
|
else:
|
|
report = run_pipeline(
|
|
args.pdf,
|
|
out_dir=out_dir,
|
|
project_input=project_input or None,
|
|
source_name=os.path.basename(args.pdf),
|
|
)
|
|
|
|
s = report["summary"]
|
|
print("\n" + "=" * 60)
|
|
print(f" Conflicts found: {s['conflicts_found']} "
|
|
f"(high {s['by_severity']['high']}, "
|
|
f"medium {s['by_severity']['medium']}, "
|
|
f"low {s['by_severity']['low']})")
|
|
if s.get("agent_status") == "needs_review":
|
|
print(" Stopped for human review - finalize via the web UI, "
|
|
"or rerun with --no-review.")
|
|
else:
|
|
print(f" Report: {os.path.join(out_dir, 'report.md')}")
|
|
print("=" * 60)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|