Set writable YOLO config directory
This commit is contained in:
@@ -10,6 +10,7 @@ YOLO_MODEL_PATH=
|
||||
YOLO_MODEL_ID=yolo-configured
|
||||
YOLO_MODEL_DISPLAY_NAME=Configured YOLO detector
|
||||
YOLO_MODEL_VERSION=
|
||||
YOLO_CONFIG_DIR=./storage/ultralytics
|
||||
YOLO_DEVICE=cpu
|
||||
YOLO_IMAGE_SIZE=640
|
||||
YOLO_MAX_TILES=100
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
- Added a full-workflow run mode selector so repeated Map QA/QC runs can reuse the latest saved derived dataset instead of creating duplicate dataset/export artifacts.
|
||||
- Added an opt-in Docker/Unraid AI build path (`GEOINTEL_INSTALL_AI=true`) for installing optional PyTorch/Ultralytics dependencies while keeping the default GIS runtime lightweight and import-safe.
|
||||
- Hardened the AI Docker runtime with OpenCV native libraries required by Ultralytics and made YOLO dependency detection use real imports instead of optimistic module discovery.
|
||||
- Added a writable `YOLO_CONFIG_DIR` default under application storage so Ultralytics does not fall back to root user config paths in Docker/Unraid.
|
||||
- Added static regression coverage for the road basemap, attribution, basemap policy notice, database layer selector and persisted operational GIS workflow wiring.
|
||||
|
||||
## Sprint 115 QA/QC and Exports usability layout pass (2026-07-04)
|
||||
|
||||
@@ -231,6 +231,9 @@ AI-enabled Docker images include the native OpenCV runtime libraries required by
|
||||
Ultralytics. Dependency availability is checked with real `torch` and
|
||||
`ultralytics` imports, so missing shared libraries are reported as
|
||||
`dependency_unavailable` instead of being treated as configured.
|
||||
Docker/Unraid runtimes set `YOLO_CONFIG_DIR` to a writable storage path so
|
||||
Ultralytics does not attempt to write settings under the root user config
|
||||
directory.
|
||||
|
||||
Configured YOLO requires:
|
||||
|
||||
@@ -269,6 +272,7 @@ Optional tuning:
|
||||
YOLO_MODEL_ID=yolo-configured
|
||||
YOLO_MODEL_DISPLAY_NAME="Configured YOLO detector"
|
||||
YOLO_MODEL_VERSION=local-v1
|
||||
YOLO_CONFIG_DIR=/app/storage/ultralytics
|
||||
YOLO_DEVICE=cpu
|
||||
YOLO_IMAGE_SIZE=640
|
||||
YOLO_MAX_TILES=100
|
||||
|
||||
@@ -81,6 +81,7 @@ def test_env_example_uses_runtime_env_names_read_by_backend_and_frontend() -> No
|
||||
assert "GEOINTEL_INSTALL_AI=false" in env_example
|
||||
assert "YOLO_ENABLED=false" in env_example
|
||||
assert "YOLO_MODEL_PATH=" in env_example
|
||||
assert "YOLO_CONFIG_DIR=./storage/ultralytics" in env_example
|
||||
assert "YOLO_MAX_TILES=100" in env_example
|
||||
assert "ENABLE_YOLO" not in env_example
|
||||
assert "ENABLE_SAM" not in env_example
|
||||
@@ -154,6 +155,22 @@ def test_backend_docker_start_script_waits_for_sql_connection_before_migrations(
|
||||
assert "uvicorn app.main:app --host 0.0.0.0 --port 8000" in script
|
||||
|
||||
|
||||
def test_runtime_sets_writable_ultralytics_config_directory() -> None:
|
||||
compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")
|
||||
unraid_compose = (ROOT / "docker-compose.unraid.yml").read_text(encoding="utf-8")
|
||||
start_script = (ROOT / "deploy" / "unraid" / "all-in-one-start.sh").read_text(encoding="utf-8")
|
||||
run_script = (ROOT / "deploy" / "unraid" / "run-dockerman-container.sh").read_text(encoding="utf-8")
|
||||
unraid_env = (ROOT / "deploy" / "unraid" / "geointel.env.example").read_text(encoding="utf-8")
|
||||
|
||||
assert "YOLO_CONFIG_DIR: ${YOLO_CONFIG_DIR:-/app/storage/ultralytics}" in compose
|
||||
assert "YOLO_CONFIG_DIR: ${YOLO_CONFIG_DIR:-/app/storage/ultralytics}" in unraid_compose
|
||||
assert 'export YOLO_CONFIG_DIR="${YOLO_CONFIG_DIR:-$STORAGE_ROOT/ultralytics}"' in start_script
|
||||
assert 'mkdir -p "$PGDATA" "$STORAGE_ROOT" "$YOLO_CONFIG_DIR"' in start_script
|
||||
assert 'YOLO_CONFIG_DIR="${YOLO_CONFIG_DIR:-/app/storage/ultralytics}"' in run_script
|
||||
assert '-e YOLO_CONFIG_DIR="$YOLO_CONFIG_DIR"' in run_script
|
||||
assert "YOLO_CONFIG_DIR=/app/storage/ultralytics" in unraid_env
|
||||
|
||||
|
||||
def test_docker_build_contexts_exclude_vendor_build_and_cache_outputs() -> None:
|
||||
required_patterns = {
|
||||
"node_modules",
|
||||
|
||||
@@ -88,6 +88,8 @@ GIS-only image. Set `GEOINTEL_INSTALL_AI=true`, mount models through
|
||||
`YOLO_MODEL_PATH=/app/models/<model>.pt` only when you have a local model file.
|
||||
The AI-enabled image installs PyTorch/Ultralytics plus the native OpenCV runtime
|
||||
libraries needed for Ultralytics imports; it still never downloads model weights.
|
||||
`YOLO_CONFIG_DIR` defaults to `/app/storage/ultralytics`, a writable persistent
|
||||
path, so Ultralytics settings do not fall back to root user config directories.
|
||||
|
||||
Validate:
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ export STORAGE_ROOT="${STORAGE_ROOT:-${GEOINTEL_STORAGE_ROOT:-/app/storage}}"
|
||||
export DATABASE_URL="${DATABASE_URL:-postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@127.0.0.1:5432/${POSTGRES_DB}}"
|
||||
export CORS_ORIGINS="${GEOINTEL_CORS_ORIGINS:-${CORS_ORIGINS:-http://localhost:1202,http://127.0.0.1:1202}}"
|
||||
export MAX_UPLOAD_MB="${GEOINTEL_MAX_UPLOAD_MB:-${MAX_UPLOAD_MB:-500}}"
|
||||
export YOLO_CONFIG_DIR="${YOLO_CONFIG_DIR:-$STORAGE_ROOT/ultralytics}"
|
||||
|
||||
mkdir -p "$PGDATA" "$STORAGE_ROOT" /run/nginx /var/log/nginx
|
||||
mkdir -p "$PGDATA" "$STORAGE_ROOT" "$YOLO_CONFIG_DIR" /run/nginx /var/log/nginx
|
||||
chown -R postgres:postgres "$PGDATA"
|
||||
|
||||
postgres_pid=""
|
||||
|
||||
@@ -31,6 +31,7 @@ YOLO_MODEL_PATH=
|
||||
YOLO_MODEL_ID=yolo-configured
|
||||
YOLO_MODEL_DISPLAY_NAME=Configured YOLO detector
|
||||
YOLO_MODEL_VERSION=
|
||||
YOLO_CONFIG_DIR=/app/storage/ultralytics
|
||||
YOLO_DEVICE=cpu
|
||||
YOLO_IMAGE_SIZE=640
|
||||
YOLO_MAX_TILES=100
|
||||
|
||||
@@ -25,6 +25,7 @@ YOLO_MODEL_PATH="${YOLO_MODEL_PATH:-}"
|
||||
YOLO_MODEL_ID="${YOLO_MODEL_ID:-yolo-configured}"
|
||||
YOLO_MODEL_DISPLAY_NAME="${YOLO_MODEL_DISPLAY_NAME:-Configured YOLO detector}"
|
||||
YOLO_MODEL_VERSION="${YOLO_MODEL_VERSION:-}"
|
||||
YOLO_CONFIG_DIR="${YOLO_CONFIG_DIR:-/app/storage/ultralytics}"
|
||||
YOLO_DEVICE="${YOLO_DEVICE:-cpu}"
|
||||
YOLO_IMAGE_SIZE="${YOLO_IMAGE_SIZE:-640}"
|
||||
YOLO_MAX_TILES="${YOLO_MAX_TILES:-100}"
|
||||
@@ -83,6 +84,7 @@ docker run -d \
|
||||
-e YOLO_MODEL_ID="$YOLO_MODEL_ID" \
|
||||
-e YOLO_MODEL_DISPLAY_NAME="$YOLO_MODEL_DISPLAY_NAME" \
|
||||
-e YOLO_MODEL_VERSION="$YOLO_MODEL_VERSION" \
|
||||
-e YOLO_CONFIG_DIR="$YOLO_CONFIG_DIR" \
|
||||
-e YOLO_DEVICE="$YOLO_DEVICE" \
|
||||
-e YOLO_IMAGE_SIZE="$YOLO_IMAGE_SIZE" \
|
||||
-e YOLO_MAX_TILES="$YOLO_MAX_TILES" \
|
||||
|
||||
@@ -23,6 +23,7 @@ services:
|
||||
YOLO_MODEL_ID: ${YOLO_MODEL_ID:-yolo-configured}
|
||||
YOLO_MODEL_DISPLAY_NAME: ${YOLO_MODEL_DISPLAY_NAME:-Configured YOLO detector}
|
||||
YOLO_MODEL_VERSION: ${YOLO_MODEL_VERSION:-}
|
||||
YOLO_CONFIG_DIR: ${YOLO_CONFIG_DIR:-/app/storage/ultralytics}
|
||||
YOLO_DEVICE: ${YOLO_DEVICE:-cpu}
|
||||
YOLO_IMAGE_SIZE: ${YOLO_IMAGE_SIZE:-640}
|
||||
YOLO_MAX_TILES: ${YOLO_MAX_TILES:-100}
|
||||
|
||||
@@ -28,6 +28,7 @@ services:
|
||||
YOLO_MODEL_ID: ${YOLO_MODEL_ID:-yolo-configured}
|
||||
YOLO_MODEL_DISPLAY_NAME: ${YOLO_MODEL_DISPLAY_NAME:-Configured YOLO detector}
|
||||
YOLO_MODEL_VERSION: ${YOLO_MODEL_VERSION:-}
|
||||
YOLO_CONFIG_DIR: ${YOLO_CONFIG_DIR:-/app/storage/ultralytics}
|
||||
YOLO_DEVICE: ${YOLO_DEVICE:-cpu}
|
||||
YOLO_IMAGE_SIZE: ${YOLO_IMAGE_SIZE:-640}
|
||||
YOLO_MAX_TILES: ${YOLO_MAX_TILES:-100}
|
||||
|
||||
@@ -6,6 +6,7 @@ Changed:
|
||||
- Added opt-in Docker and Unraid AI build support through `GEOINTEL_INSTALL_AI=true`; default builds still install only the GIS runtime.
|
||||
- Passed YOLO runtime environment variables and a `/app/models` volume into the all-in-one Unraid container so local PyTorch/Ultralytics models can be mounted explicitly.
|
||||
- Hardened the AI image path after Tower validation showed `torch` imported but `ultralytics` failed on a missing OpenCV native library. The Dockerfiles now include the required OpenCV runtime shared libraries and YOLO dependency detection performs real imports instead of `find_spec` checks.
|
||||
- Added a writable `YOLO_CONFIG_DIR` default under application storage after Tower validation showed Ultralytics otherwise falls back to `/tmp` because root config is not writable in the container.
|
||||
- Updated `.env.example`, `backend/README.md`, `frontend/README.md`, `scripts/README.md`, `docs/AI_PIPELINES.md`, `docs/TODO.md` and `CHANGELOG.md`.
|
||||
- Added regression coverage in `backend/tests/test_sprint116_operational_gis_map_workflow.py`, `backend/tests/test_sprint8b_yolo_foundation.py` and `backend/tests/test_docker_runtime_config.py`.
|
||||
|
||||
@@ -19,6 +20,8 @@ Validation:
|
||||
- `python -m pytest backend\tests\test_sprint31_unraid_template.py backend\tests\test_docker_runtime_config.py -q` passed: 27 tests.
|
||||
- RED: `python -m pytest backend\tests\test_sprint8b_yolo_foundation.py backend\tests\test_docker_runtime_config.py -q` failed before the runtime hardening because YOLO dependency detection still used `find_spec` and the Dockerfiles lacked OpenCV native runtime libraries.
|
||||
- `python -m pytest backend\tests\test_sprint8b_yolo_foundation.py backend\tests\test_docker_runtime_config.py -q` passed: 30 tests.
|
||||
- RED: `python -m pytest backend\tests\test_docker_runtime_config.py -q` failed before `YOLO_CONFIG_DIR` wiring because the Compose, Unraid and startup paths did not define a writable Ultralytics config directory.
|
||||
- `python -m pytest backend\tests\test_docker_runtime_config.py -q` passed: 21 tests.
|
||||
- `python -m compileall backend/app` passed after the AI runtime hardening.
|
||||
- `cd backend && python -m pytest -q` passed: 367 tests.
|
||||
- `cd frontend && npm run typecheck` passed.
|
||||
|
||||
Reference in New Issue
Block a user