Expand session notes with API, job log, and model selection details.
Keep NOTES.md current for new sessions after the job-log and dual-model UI work. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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_id>/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/<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`.
|
||||
3. **Jobs are in-memory** — Process restart clears job status; `outputs/<job_id>/` (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_id>/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/<job_id>/`.
|
||||
- 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`.
|
||||
|
||||
Reference in New Issue
Block a user