Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/docs-links.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Docs External Links

# External links rot without a code change, so this runs on a schedule and by hand rather
# than on every PR. Relative links and anchors are checked offline by
# scripts/docs/check_docs_vs_code.py (pre-commit and the test suite).

on:
schedule:
- cron: '0 7 * * 1'
workflow_dispatch:

permissions:
contents: read

jobs:
external-links:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Check external links
uses: lycheeverse/lychee-action@v2
with:
args: >-
--no-progress --scheme https --max-retries 2 --timeout 20
--exclude '^https?://(localhost|127\.0\.0\.1|0\.0\.0\.0|example\.)'
README.md INSTALLATION.md SECURITY.md CONTRIBUTING.md docs docs-site/docs
fail: true
5 changes: 5 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ on:
- master
paths:
- 'docs-site/**'
- 'docs/**'
- '.github/workflows/docs.yml'
pull_request:
branches:
- main
- master
paths:
- 'docs-site/**'
- 'docs/**'
- '.github/workflows/docs.yml'
workflow_dispatch:

Expand Down Expand Up @@ -59,6 +61,9 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Type-check
run: npm run typecheck

- name: Build documentation
run: npm run build

Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ docker-compose.*.yml
# (not generated) -- restores the checkout hot-reload workflow that the
# now deploy-safe docker-compose.yml no longer carries by default.
!docker-compose.dev.yml
# docker-compose.override.yml is generated by setup.sh - do not commit
# Your own compose overrides, loaded by ./openprocessor and make (docs-site: ingest-large-folder)
docker-compose.local.yml

# Environment template output (auto-generated by setup)
# Note: env.template is committed, .env is generated
Expand Down
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ repos:
pass_filenames: false
always_run: true

- id: check-cited-paths
name: Comments and docstrings cite files that exist
entry: python3 scripts/codegen/check_cited_paths.py
language: system
pass_filenames: false
always_run: true

- id: check-vulture
name: No new unused code (vulture)
entry: bash -c 'py=python3; [ -x .venv/bin/python ] && py=.venv/bin/python; exec "$py" scripts/codegen/check_vulture.py'
Expand Down
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@ history of this codebase and was never published. This release is `[0.5.0]`.

### Added

- `./openprocessor` and `make` load an optional, git-ignored `docker-compose.local.yml` after the
stock compose files: a second read-only source root, a changed `shm_size` or a mounted credentials
file no longer need hand-built `docker compose` commands (#283).
- `./openprocessor curation up [service...]` and `make curation-up SERVICES="..."` start a subset of the
curation services; a name that is not a curation service is refused (#285).
- `capture_docs_screenshots.py` requires `--public-projects` (`OP_CROPWRIGHT_PUBLIC_PROJECTS`), refuses
a `--project` outside it, and refuses the pages that list every project (project list, combine,
copy-settings, clone-from, model sharing) while the backend holds any project outside it (#284).
- `scripts/docs/check_docs_vs_code.py` checks `./openprocessor` commands and flags, `make` targets,
`<Screenshot>` file names and `NAME=` lines in fenced blocks against the code; the version test
scans the frontend docs; `docs.yml` also runs on `docs/**` and type-checks the site; a weekly
`docs-links.yml` checks external links (#287, #293).
- `scripts/codegen/check_cited_paths.py` (pre-commit): a comment or docstring that cites a `docs/`,
`scripts/` or similar file cites one that exists. Citations of design notes that left the tree
in the frontend sit in a shrink-only baseline (#289).
- Bulk-ingest preflight (#278): `POST /curation/projects/{project}/ingest/bulk/preview`,
`scripts/curation/bulk_ingest.py --preview` and a Preview button on the Cropwright Ingest page report,
without ingesting anything, a folder's, manifest's, S3 prefix's or registered source's file count, total
Expand Down Expand Up @@ -151,6 +166,18 @@ history of this codebase and was never published. This release is `[0.5.0]`.

### Changed

- `./openprocessor profile <name>` now sets `GPU_PROFILE` and `OP_INGEST_GPU_DECODE` in an existing
`.env` and names the real restart command. `setup.sh` no longer writes `docker-compose.override.yml`,
and `TRITON_WORKERS`, `MAX_BATCH_SIZE` and `SHM_SIZE` (read by nothing) are gone from `.env`,
`env.template`, the profile JSON and the docs (#281).
- `curation-evaluator` moved from the `curation` profile to its own `evaluator` profile, so
`--profile curation up -d` no longer reserves a GPU for it. Start it with
`./openprocessor curation up curation-evaluator` or `docker compose run --rm curation-evaluator` (#285).
- In a checkout, `./openprocessor` with no `COMPOSE_PROJECT_NAME` in `.env` uses the documented default
project `openprocessor` and says so, instead of exiting 3. An install directory still refuses (#291).
- `bulk_ingest.py` defaults `--api-base` to `localhost:8000` inside the container and `localhost:4603`
on the host (#288).
- `docs-site/.nvmrc` follows the Node version of `docs-site/Dockerfile` (#289).
- Repo cleanup, PR 9a (typing): mypy now runs with `disallow_untyped_defs`, `disallow_incomplete_defs`
and `warn_return_any` over `src scripts docker export` (`tests/` is outside its scope), and the
nine-error-code relax override for the ML modules is gone. 328 errors were fixed with real types
Expand Down Expand Up @@ -416,6 +443,8 @@ history of this codebase and was never published. This release is `[0.5.0]`.

### Fixed

- `docs-site` `npm run typecheck` passes: `baseUrl` (deprecated, TS5101) is dropped for `paths` alone,
and the missing `@types/react-dom` is added (#293).
- Monitoring alerts (#290): the Grafana rules **Triton Model Not Ready** (`nv_model_ready_state`),
**API High Error Rate** (`http_requests_total`) and **OpenSearch Cluster Unhealthy**
(`opensearch_cluster_status`) queried series nothing exports and could never fire; the first and last are
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ Docker socket (read its header first).
| `triton-server` | none | 4600 HTTP, 4601 gRPC, 4602 metrics | TensorRT models, explicit model control |
| `api` | none | 4603 | FastAPI, all routes |
| `opensearch` | none | 4607 | k-NN store and the curation datastore |
| `curation-detection-worker`, `curation-vlm-worker`, `curation-auto-label-worker`, `curation-cluster-refresh`, `curation-evaluator` | `curation` | | Region cascade, VLM loop, auto-label driver, clustering refresh, bake-off runner |
| `curation-detection-worker`, `curation-vlm-worker`, `curation-auto-label-worker`, `curation-cluster-refresh` | `curation` | | Region cascade, VLM loop, auto-label driver, clustering refresh |
| `curation-evaluator` | `evaluator` | | Bake-off runner, on demand (`./openprocessor curation up curation-evaluator`) |
| `segmenter` | `segmenter` | 4611 | Region proposals from a text prompt |
| `vlm` | `vlm` | 4612 | Local vLLM serving a catalog model |
| `curation-trainer`, `curation-mlflow` | `training` | 4609 (MLflow) | Training jobs through a file protocol, tracking |
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ Never add a line to hide a new finding; fix it.
| `check_layering.py` | routers import services and clients, never the reverse; clients never import services; `src/domain`, `config`, `utils` and `schemas` import neither; only routers import `fastapi`. No baseline: the one permanent exception (the guarded OpenSearch client factory) is listed with its reason in the script |
| `check_ruff_ratchet.py` | no function above complexity 15, no new `except Exception` |
| `check_process_ids.py` | no wave, round or work-package ids, and no history names, in comments, docstrings or file names |
| `check_cited_paths.py` | a comment or docstring that cites a `docs/`, `scripts/` or similar file cites one that exists |
| `check_vulture.py` | no unused code; a reviewed false positive goes in `vulture_whitelist.py` with a reason |
| `check_naming_leaks.py` | no private product or domain names in the tracked tree |
| ruff `TID251` | `src/` reads the environment only through `src/config/env.py` (`env_str`, `env_int`, `env_float`, `env_bool`, `env_path`); a malformed value raises naming the variable |
Expand Down
65 changes: 47 additions & 18 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,37 @@ To change it, edit `.env`, then recreate the three services with
`./openprocessor restart` does not re-read `.env`. Paths you pass to ingest are
container paths under `/data/source`. `OP_SOURCE_PATH_ALIASES` adds more named
roots, but compose does not mount them for you: bind-mount each one into the
same three services with your own compose override file.
same three services in your own `docker-compose.local.yml` (next).

### More than one source root and your own compose overrides

`./openprocessor` and `make` load `docker-compose.local.yml` (next to `docker-compose.yml`,
ignored by git) after the stock files, when it exists. Put anything of your own in it: a second
read-only source root, a changed `shm_size`, a mounted credentials file. A file named
`docker-compose.override.yml` is not read. Example for a second root:

```yaml
# docker-compose.local.yml (next to docker-compose.yml; git ignores it)
services:
api:
volumes:
- /srv/nas/archive:/data/archive:ro
curation-detection-worker:
volumes:
- /srv/nas/archive:/data/archive:ro
curation-auto-label-worker:
volumes:
- /srv/nas/archive:/data/archive:ro
```

and in `.env`:

```bash
OP_SOURCE_PATH_ALIASES=archive=/data/archive
```

Then `./openprocessor start` (or `make up`) recreates the services. Check the merged result with
`docker compose -p <project> -f docker-compose.yml -f docker-compose.local.yml config`.

### The `openprocessor` CLI

Expand Down Expand Up @@ -403,9 +433,9 @@ git clone https://github.com/davidamacey/OpenProcessor.git && cd OpenProcessor &

In a checkout, manage the stack with `make` (`make up`, `make curation-up`,
`make down`, `make status`). The `./openprocessor` CLI (and its shim
`./scripts/openprocessor.sh`) refuses to run until `.env` has a
`COMPOSE_PROJECT_NAME` line; the `.env` that `setup.sh` writes has none, so
add `COMPOSE_PROJECT_NAME=openprocessor` first. When you run `docker compose`
`./scripts/openprocessor.sh`) uses the project `openprocessor` and warns when `.env` has no
`COMPOSE_PROJECT_NAME` line (the `.env` that `setup.sh` writes has none); set the line to
choose another name. When you run `docker compose`
by hand, pass `-p <project>` so you never act on another stack that uses the
default name.

Expand Down Expand Up @@ -520,10 +550,9 @@ and **standard**. Set it yourself in `.env` to override; `openprocessor gpu plan
- The Triton load list is fixed in `docker-compose.yml`. A model that was never exported stays
unloaded and Triton keeps running. `scripts/setup.sh --profile=minimal` exports the
essential models only, with no OCR.
- `.env` also gets `TRITON_WORKERS`, `MAX_BATCH_SIZE` and `SHM_SIZE`, but nothing reads them.
The API runs 32 uvicorn workers and each service has `shm_size: 8g` in `docker-compose.yml`.
- `setup.sh` writes `docker-compose.override.yml`, but it is never loaded: `setup.sh`,
`./openprocessor` and `make` pass explicit `-f` files.
- The API runs 32 uvicorn workers and each service has `shm_size: 8g` in `docker-compose.yml`,
whatever the profile says. To change either, use your own `docker-compose.local.yml` (see
[More than one source root and your own compose overrides](#more-than-one-source-root-and-your-own-compose-overrides)).

### Selecting a Profile

Expand All @@ -532,20 +561,20 @@ and **standard**. Set it yourself in `.env` to override; `openprocessor gpu plan
./scripts/setup.sh --profile=minimal
```

Switching later with `./openprocessor profile <name>` (a checkout only) is incomplete
(issue #281). It rewrites the Triton configs but, when `.env` already exists, leaves
`GPU_PROFILE` and `OP_INGEST_GPU_DECODE` unchanged. Switch by hand:
Switch later with `./openprocessor profile <name>` (a checkout only). It rewrites the Triton
configs and sets `GPU_PROFILE` and `OP_INGEST_GPU_DECODE` in the existing `.env`, leaving every
other line alone:

```bash
./openprocessor profile standard # rewrites models/*/config.pbtxt
# edit .env: GPU_PROFILE=standard and OP_INGEST_GPU_DECODE=0 (or 1)
./openprocessor profile standard # rewrites models/*/config.pbtxt, updates .env
./openprocessor restart triton-server
```

Do not pass the optional second argument (a GPU id); it is written into every `config.pbtxt`.
A `.env` made by `scripts/setup.sh` or copied from `env.template` must also have
`COMPOSE_PROJECT_NAME` set, or `./openprocessor` refuses to run (see
[Configure the environment](#4-configure-environment)).
In a checkout, a `.env` without `COMPOSE_PROJECT_NAME` runs the default project `openprocessor`
and says so; set `COMPOSE_PROJECT_NAME` to use another (see
[Configure the environment](#4-configure-environment)). An install directory still refuses to run
without it.
See [Deployment and GPU sizing](docs-site/docs/deployment/gpu-sizing.mdx) for the details.

---
Expand Down Expand Up @@ -591,7 +620,7 @@ cp env.template .env
# Edit .env to adjust settings
```

The `./openprocessor` CLI also needs the compose project name in `.env`:
To name the compose project yourself (the `./openprocessor` CLI otherwise uses `openprocessor`):

```bash
echo 'COMPOSE_PROJECT_NAME=openprocessor' >> .env
Expand Down Expand Up @@ -804,7 +833,7 @@ docker compose down --rmi all
# Remove generated files
rm -rf pytorch_models/*.pt pytorch_models/*.onnx
rm -rf models/*/1/model.plan
rm -rf .env docker-compose.override.yml
rm -rf .env docker-compose.local.yml
rm -rf cache/ logs/ outputs/ test_results/
```

Expand Down
14 changes: 9 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ SHELL := /bin/bash
# checkout, so it always adds that overlay -- detected by src/main.py
# existing next to the compose file (same check scripts/setup.sh and
# scripts/openprocessor.sh use).
DEV_COMPOSE := docker compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.gpu-arbiter.yml --profile curation --profile segmenter --profile vlm --profile training
COMPOSE := docker compose $(if $(wildcard src/main.py),-f docker-compose.yml -f docker-compose.dev.yml,-f docker-compose.yml)
DEV_DEV_COMPOSE := docker compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.gpu-arbiter.yml --profile curation --profile segmenter --profile vlm --profile training
# The curation services `curation-up SERVICES=...` accepts (the evaluator is on demand).
CURATION_SERVICES := curation-detection-worker curation-vlm-worker curation-auto-label-worker curation-cluster-refresh curation-evaluator
COMPOSE := docker compose $(if $(wildcard src/main.py),-f docker-compose.yml -f docker-compose.dev.yml,-f docker-compose.yml)$(if $(wildcard docker-compose.local.yml), -f docker-compose.local.yml)
V := .venv/bin
# Prefer the project venv's interpreter when it exists; fall back to
# system python3 for a fresh checkout that hasn't created .venv yet.
Expand Down Expand Up @@ -1125,11 +1127,12 @@ docs-build: ## Build docs-site (npm ci + docusaurus build; broken links and anch
cd docs-site && npm ci && npm run build

DOCS_PROJECT ?= coco-bench-2k
DOCS_PUBLIC_PROJECTS ?= $(DOCS_PROJECT)

.PHONY: docs-screenshots
docs-screenshots: ## Capture the Cropwright docs screenshots (OP_CROPWRIGHT_URL=... required; public sample project only)
@test -n "$(OP_CROPWRIGHT_URL)" || { echo "Set OP_CROPWRIGHT_URL to a Cropwright instance that holds only public sample data."; exit 2; }
cd frontend && e2e/.venv/bin/python scripts/capture_docs_screenshots.py --base-url $(OP_CROPWRIGHT_URL) --project $(DOCS_PROJECT)
cd frontend && e2e/.venv/bin/python scripts/capture_docs_screenshots.py --base-url $(OP_CROPWRIGHT_URL) --project $(DOCS_PROJECT) --public-projects $(DOCS_PUBLIC_PROJECTS)

.PHONY: docs
docs: info ## Alias for info
Expand Down Expand Up @@ -1178,9 +1181,10 @@ clone-ref: ## Clone a specific reference repo (usage: make clone-ref REPO=ultral
# never touch them. Use the targets below instead.

.PHONY: curation-up
curation-up: ## Start the curation worker services (experimental, opt-in)
curation-up: ## Start the curation workers (all, or SERVICES="curation-vlm-worker ...")
$(if $(filter-out $(CURATION_SERVICES),$(SERVICES)),$(error Not a curation service: $(filter-out $(CURATION_SERVICES),$(SERVICES)). Choose from: $(CURATION_SERVICES)))
@echo "Starting curation subsystem (profile: curation)..."
$(COMPOSE) --profile curation up -d
$(COMPOSE) --profile curation up -d $(SERVICES)
@echo ""
@echo "Curation services starting. Check status with: make curation-status"

Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,7 @@ make up && make curation-up
# 3. --folder is a container path under the /data/source mount, not a
# host-relative one:
docker compose -p openprocessor exec api python scripts/curation/bulk_ingest.py \
--folder /data/source/coco_va_readme/images --project cars \
--api-base http://localhost:8000/curation
--folder /data/source/coco_va_readme/images --project cars
```

An installed directory already has `OP_SOURCE_ROOT_HOST=./data`, so skip step 2
Expand Down Expand Up @@ -569,8 +568,8 @@ Client (Port 4603)
- `triton-server` (4600-4602): Triton with TensorRT models.
- `opensearch` (4607): vector database and the curation datastore.
- `curation-detection-worker`, `curation-vlm-worker`,
`curation-auto-label-worker`, `curation-cluster-refresh`,
`curation-evaluator` [`curation`].
`curation-auto-label-worker`, `curation-cluster-refresh` [`curation`];
`curation-evaluator` [`evaluator`, on demand].
- `segmenter` (4611) [`segmenter`], `vlm` (4612) [`vlm`].
- `curation-trainer`, `curation-mlflow` (4609) [`training`].
- Prometheus (4604), Grafana (4605), Loki (4606), OpenSearch Dashboards (4608),
Expand Down
2 changes: 0 additions & 2 deletions config_templates/profiles/full.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
"instance_count": 4,
"max_batch_size": 64,
"preferred_batch_sizes": [8, 16, 32, 64],
"shm_size": "8g",
"workers": 32,
"ocr_pipeline_instances": 4,
"ingest_gpu_decode": true,
"python_backend_budget_mib": 2048,
Expand Down
2 changes: 0 additions & 2 deletions config_templates/profiles/minimal.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
"instance_count": 1,
"max_batch_size": 16,
"preferred_batch_sizes": [4, 8, 16],
"shm_size": "4g",
"workers": 8,
"ocr_pipeline_instances": 1,
"ingest_gpu_decode": false,
"python_backend_budget_mib": 512,
Expand Down
2 changes: 0 additions & 2 deletions config_templates/profiles/standard.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
"instance_count": 2,
"max_batch_size": 32,
"preferred_batch_sizes": [8, 16, 32],
"shm_size": "6g",
"workers": 16,
"ocr_pipeline_instances": 2,
"ingest_gpu_decode": false,
"python_backend_budget_mib": 1024,
Expand Down
7 changes: 4 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -606,14 +606,15 @@ services:
networks:
- triton_net

# On-demand only (no `restart:` — see docker/evaluator/Dockerfile):
# `docker compose --profile curation run --rm curation-evaluator`.
# On-demand only (no `restart:` — see docker/evaluator/Dockerfile): its own
# profile, so `--profile curation up -d` does not reserve a GPU for it.
# `docker compose run --rm curation-evaluator`.
curation-evaluator:
image: ${OP_EVALUATOR_IMAGE:-${OP_IMAGE_REPO:-davidamacey}/openprocessor-evaluator:${OP_IMAGE_TAG:-0.5.0}}
container_name: ${COMPOSE_PROJECT_NAME:-openprocessor}-evaluator
logging: *default-logging
profiles:
- curation
- evaluator
env_file:
- path: ./.env
required: false
Expand Down
6 changes: 3 additions & 3 deletions docker/evaluator/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
# build wheel dependencies stay in the builder and never reach the runtime image.
#
# Build:
# docker compose --profile curation build curation-evaluator
# docker compose build curation-evaluator
#
# Run on demand (watch mode; scans every projects/*/bakeoff_jobs under the state dir):
# docker compose --profile curation run --rm curation-evaluator
# docker compose run --rm curation-evaluator
# One-shot job:
# docker compose --profile curation run --rm curation-evaluator \
# docker compose run --rm curation-evaluator \
# python -m scripts.curation.bakeoff.bakeoff_runner --job /eval_jobs/x.job.json
# =============================================================================

Expand Down
Loading
Loading