# Session Notes — Conflict Checker Orientation for a new coding session. Setup and Docker details live in [README.md](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](backend/main.py)) | | UI | Single static file [frontend/index.html](frontend/index.html), served by FastAPI | | Pipeline | Shared by web + CLI: [backend/pipeline/runner.py](backend/pipeline/runner.py) | | LLM | OpenRouter via `openai` SDK; default `google/gemini-2.5-pro`. Vision always OpenRouter; text stages can use local vLLM | | PDF | `pdf2image` + system `poppler-utils` → JPEG page images | | Jobs | In-memory threads ([backend/jobs.py](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](backend/prompts.py) | | Env knobs | [backend/config.py](backend/config.py), [backend/.env.example](backend/.env.example) | | HTTP API | [backend/main.py](backend/main.py) — `/health`, `/models`, `/check`, `/jobs/{id}`, `/jobs/{id}/log`, `/jobs/{id}/sheet-image/{page}` | | CLI tuning loop | [cli/run_check.py](cli/run_check.py) | | LLM client, cache, cost, per-run model overrides | [backend/llm.py](backend/llm.py) | | OpenRouter vision/text model lists | [backend/models_catalog.py](backend/models_catalog.py) | | Job registry + stdout tee log | [backend/jobs.py](backend/jobs.py), [backend/job_log.py](backend/job_log.py) | | Stage helpers (prompt render, issue validate) | [backend/pipeline/_stage.py](backend/pipeline/_stage.py) | | Code text corpus (Stage 7) | [backend/code_corpus/](backend/code_corpus/) | | Hybrid local LLM helper | [scripts/setup_vllm.sh](scripts/setup_vllm.sh) | Older prompt snapshot: `backend/prompts.py.v1`. ## Gotchas 1. **Prompt placeholders** — Use `str.replace` via `_stage.render`, never `str.format`. Prompts contain literal `{` JSON braces. 2. **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. 3. **Jobs are in-memory** — Process restart clears job status; reports on disk under `backend/outputs//` can still be reloaded via `get_job` disk fallback. Run logs persist as `job.log` in that same folder and via `GET /jobs/{id}/log`. 4. **Dependency pin** — `httpx==0.27.2` with `openai==1.51.0`. httpx ≥0.28 breaks openai’s `proxies=` kwarg. 5. **Code corpus licensing** — Only `ada_2010.txt` is shipped. Do not paste IBC/IFC/IECC without a license (see `backend/code_corpus/README.md`). 6. **Dual assertion schema** — Newer `{sheet, objects[]}` is mapped to legacy `{assertions[]}` with `attribute`/`value` for older stages. 7. **Grounding guard** — Extractor drops objects whose numeric claims are not in `source_text` (graphical-only objects allowed). 8. **Cost counters** — Module-global LLM cost accounting; overlapping jobs share counters. Same for stdout tee logging and per-run model overrides. 9. **Samples** — `samples/*.pdf` are gitignored; drop PDFs locally for CLI runs. 10. **README drift** — Treat README for setup/CI; treat this file + `runner.py` for pipeline truth. `prompts.py` header may still say some prompts are unwired — they are wired through the runner. 11. **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. 12. **Job log** — Pipeline `print()` output is teed into memory + `outputs//job.log`. Status polls include `log_tail`; completed/error jobs include full `log`. ## Quick start pointers - Full setup: [README.md](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=true` in `backend/.env` so unchanged stages replay for free; clear with `rm -rf backend/.llm_cache`. - Artifacts: `assertions.json`, `clusters.json`, per-stage JSON, `conflicts.json`, `report.md` under the chosen `out_dir` or `backend/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`.