diff --git a/NOTES.md b/NOTES.md index 33f3650..329c19c 100644 --- a/NOTES.md +++ b/NOTES.md @@ -2,6 +2,8 @@ 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. +**Last updated:** 2026-07-31 · tip `a6b0c8f` on Gitea `main` + ## 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. @@ -51,15 +53,56 @@ PDF → images → extract → sheet index → jurisdiction Design notes for Stage 2/3 engines also live under `Changes/*.docx`. +## HTTP API (current) + +| Method | Path | Purpose | +|--------|------|---------| +| GET | `/health` | Liveness + default `model` / `text_model` + key/email flags | +| GET | `/models` | OpenRouter catalog split into `vision[]` / `text[]` + `defaults` (cached ~1h) | +| POST | `/check` | Upload PDF; returns `{job_id}` immediately | +| GET | `/jobs/{id}` | Status poll. Running: `stage` + `log_tail`. Done/error: `report` and/or `error` + full `log` | +| GET | `/jobs/{id}/log` | Full run log JSON (`lines`, `text`); `?plain=1` for text/plain | +| GET | `/jobs/{id}/sheet-image/{page}` | JPEG of source PDF page for the sheet viewer | +| GET | `/` | Serves `frontend/index.html` | + +### `/check` form fields + +- Required: `file` (PDF) +- Optional: `notification_email`, `project_name`, `address`, `occupancy`, `work_type` +- Compute: `text_local` (`true` = hybrid local text) +- Models: `vision_model`, `text_model` (OpenRouter ids; blank = config defaults) + +## Job logs + +Pipeline `print()` is teed for the job thread ([backend/job_log.py](backend/job_log.py)): + +- Live: `GET /jobs/{id}` → `log_tail` (last 80 lines) +- Done/error: same payload includes full `log` +- Disk: `backend/outputs//job.log` (survives restart; status registry does not) +- API: `GET /jobs/{id}/log` or `?plain=1` +- UI: “Run log” panel updates while running; stays visible after finish/fail + +## Vision vs text models + +Two models, not one: + +| Role | Config | Stages | Backend | +|------|--------|--------|---------| +| Vision | `MODEL` | Extract, conflict reason (images) | Always OpenRouter | +| Text | `TEXT_MODEL` (falls back to `MODEL`) | Sheet index, jurisdiction, normalize, cluster(LLM), QAQC, code, construct, validate, risk, RFI | OpenRouter, or local when hybrid | + +UI: two dropdowns filled from `GET /models` ([backend/models_catalog.py](backend/models_catalog.py)). Per-run picks go through `set_model_overrides()` in [backend/llm.py](backend/llm.py); runner clears them in `finally`. Hybrid: text dropdown also names the local model override and OpenRouter fallback. + ## 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}` | +| HTTP API | [backend/main.py](backend/main.py) | +| UI (upload, models, live log, results) | [frontend/index.html](frontend/index.html) | | 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) | +| LLM client, cache, cost, 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) | @@ -72,25 +115,33 @@ Older prompt snapshot: `backend/prompts.py.v1`. 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`. +3. **Jobs are in-memory** — Process restart clears job status; `outputs//` (report + `job.log` + `source.pdf`) still reload via disk fallback. 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. +8. **Module globals** — LLM cost counters, stdout tee, and model overrides are process-global; overlapping jobs can interleave (single-user tool assumption). 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`. +11. **Git identity** — This box has no `user.name` / `user.email`; commits need `GIT_AUTHOR_*` / `GIT_COMMITTER_*` env vars (do not `git config`). Remote push to Gitea works. +12. **No local Python deps on host** — App is meant to run in Docker; bare `python3` imports may miss `dotenv` / `httpx`. Prefer `docker compose`. ## 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/`. +- Artifacts per job: `assertions.json`, `clusters.json`, per-stage JSON, `conflicts.json`, `report.md`, `job.log`, `source.pdf` under `backend/outputs//`. - No automated test suite; validate via CLI dumps and golden-set diffs (described in README). +## Recent work (2026-07-31) + +Shipped on `main` as `a6b0c8f`: + +- Per-job run log (tee + disk + API + UI) +- Separate vision/text model dropdowns backed by OpenRouter `/models` +- Session notes file (this doc) + ## 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`.