Add per-job run logs and separate vision/text model selection.
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>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
# 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/<job_id>/` 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_id>/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`.
|
||||
Reference in New Issue
Block a user