Capture pipeline stdout into job.log + API/UI so failed runs can be reviewed, and let users pick OpenRouter vision vs text models independently. Co-authored-by: Cursor <cursoragent@cursor.com>
6.4 KiB
Session Notes — Conflict Checker
Orientation for a new coding session. Setup and Docker details live in README.md. This file tracks what the code actually does and what tends to waste time.
What this is
Cross-discipline design contradiction / senior-architect QAQC for construction drawing PDFs (Arch, Struct, Mech, Elec, Plumb, FP, etc.). Flags disagreements between disciplines before a set goes to bid/permit.
Not IronBid’s scope-ownership conflict checker.
Source of truth: Scout IT Gitea — gitea.scoutitsystems.com/woogi/Conflict_Checker.
Stack
| Layer | Detail |
|---|---|
| API | Python 3.12, FastAPI, Uvicorn (backend/main.py) |
| UI | Single static file frontend/index.html, served by FastAPI |
| Pipeline | Shared by web + CLI: backend/pipeline/runner.py |
| LLM | OpenRouter via openai SDK; default google/gemini-2.5-pro. Vision always OpenRouter; text stages can use local vLLM |
pdf2image + system poppler-utils → JPEG page images |
|
| Jobs | In-memory threads (backend/jobs.py) — no Redis/DB |
| Deploy | Docker Compose; app on port 8099 |
Live pipeline (authoritative)
README still describes an older 5-stage extract-then-compare loop. Trust runner.py. Actual flow:
PDF → images → extract → sheet index → jurisdiction
→ normalize → project intelligence (GOIDs)
→ cluster → conflict reason
→ QAQC / code / constructability
→ dedup-validate → risk → RFIs → report
| Runner stage | Module | Notes |
|---|---|---|
| PDF → images | pdf_processor |
Rasterize |
| Extract assertions | extractor |
Vision, per sheet |
| Classify sheet index | sheet_index |
LLM |
| Jurisdiction profile | jurisdiction |
After cover meta |
| Normalize | normalizer |
LLM batches |
| Project intelligence | normalizer.build_project_intelligence |
GOIDs + relationships |
| Cluster | llm_clusterer or clusterer |
Default CLUSTERER=llm |
| Conflicts | conflict_checker |
Per-cluster vision reason |
| QAQC / code / constructability | qaqc_review, code_review, constructability |
Full-set / batched |
| Validate & dedup | validator |
Merges conflict + QAQC + code + construct issues |
| Risk / RFIs | risk, rfi |
Text-only |
| Report | report |
conflicts.json + report.md (+ stage JSON dumps when out_dir set) |
Design notes for Stage 2/3 engines also live under Changes/*.docx.
Where to change what
| Concern | File |
|---|---|
| Prompts, vocab, conflict taxonomy | backend/prompts.py |
| Env knobs | backend/config.py, backend/.env.example |
| HTTP API | backend/main.py — /health, /models, /check, /jobs/{id}, /jobs/{id}/log, /jobs/{id}/sheet-image/{page} |
| CLI tuning loop | cli/run_check.py |
| LLM client, cache, cost, per-run model overrides | backend/llm.py |
| OpenRouter vision/text model lists | backend/models_catalog.py |
| Job registry + stdout tee log | backend/jobs.py, backend/job_log.py |
| Stage helpers (prompt render, issue validate) | backend/pipeline/_stage.py |
| Code text corpus (Stage 7) | backend/code_corpus/ |
| Hybrid local LLM helper | scripts/setup_vllm.sh |
Older prompt snapshot: backend/prompts.py.v1.
Gotchas
- Prompt placeholders — Use
str.replacevia_stage.render, neverstr.format. Prompts contain literal{JSON braces. - Clustering — Default is LLM (
CLUSTERER=llm); empty LLM result falls back to deterministic. Deterministic clusters need ≥2 disciplines (or schedule-vs-plan); single-discipline “missing” gaps are a known limit. - Jobs are in-memory — Process restart clears job status; reports on disk under
backend/outputs/<job_id>/can still be reloaded viaget_jobdisk fallback. Run logs persist asjob.login that same folder and viaGET /jobs/{id}/log. - Dependency pin —
httpx==0.27.2withopenai==1.51.0. httpx ≥0.28 breaks openai’sproxies=kwarg. - Code corpus licensing — Only
ada_2010.txtis shipped. Do not paste IBC/IFC/IECC without a license (seebackend/code_corpus/README.md). - Dual assertion schema — Newer
{sheet, objects[]}is mapped to legacy{assertions[]}withattribute/valuefor older stages. - Grounding guard — Extractor drops objects whose numeric claims are not in
source_text(graphical-only objects allowed). - Cost counters — Module-global LLM cost accounting; overlapping jobs share counters. Same for stdout tee logging and per-run model overrides.
- Samples —
samples/*.pdfare gitignored; drop PDFs locally for CLI runs. - README drift — Treat README for setup/CI; treat this file +
runner.pyfor pipeline truth.prompts.pyheader may still say some prompts are unwired — they are wired through the runner. - Two models — Vision (
MODEL, image stages) and text (TEXT_MODEL, non-image). UI exposes separate dropdowns from OpenRouter’s/models(cached ~1h). Hybrid still runs vision on OpenRouter; text dropdown also covers local name override + cloud fallback. - Job log — Pipeline
print()output is teed into memory +outputs/<job_id>/job.log. Status polls includelog_tail; completed/error jobs include fulllog.
Quick start pointers
- Full setup: README.md (
docker compose up -d --build→ http://localhost:8099). - Local CLI:
python cli/run_check.py samples/your_set.pdf --out out/your_set. - Prompt iteration: set
LLM_CACHE=trueinbackend/.envso unchanged stages replay for free; clear withrm -rf backend/.llm_cache. - Artifacts:
assertions.json,clusters.json, per-stage JSON,conflicts.json,report.mdunder the chosenout_dirorbackend/outputs/. - No automated test suite; validate via CLI dumps and golden-set diffs (described in README).
Conflict categories (taxonomy)
Defined in backend/prompts.py: dimensional_disagreement, elevation_disagreement, location_mismatch, missing_element, schedule_vs_plan_mismatch, detail_vs_plan_mismatch, tag_or_reference_inconsistency, spatial_clash, note_or_spec_contradiction.