Surface YOLO preflight in Detection Lab
This commit is contained in:
@@ -301,6 +301,12 @@ python scripts/yolo_preflight.py --model-path /absolute/path/to/local-model.pt -
|
||||
|
||||
The preflight checks configuration, dependency availability, local model file existence, tile manifest validity, tile count and referenced tile paths. JSON output also includes runtime diagnostics for the model directory, `YOLO_CONFIG_DIR`, installed `torch`/`ultralytics` versions and CUDA availability when dependency checks pass. It does not load a YOLO model, run inference or download weights.
|
||||
|
||||
The same read-only status is available through the API and Detection Lab UI:
|
||||
|
||||
```bash
|
||||
curl http://localhost:1202/api/v1/detection/yolo/preflight
|
||||
```
|
||||
|
||||
### Run backend
|
||||
|
||||
```bash
|
||||
|
||||
@@ -9,6 +9,7 @@ from app.db.session import get_db
|
||||
from app.schemas import DetectionQaRequest, DetectionRunRequest
|
||||
from app.services.detection_service import DetectionService
|
||||
from app.services.model_registry_service import ModelRegistryService
|
||||
from app.services.yolo_preflight_service import YoloPreflightService
|
||||
from app.utils.response import envelope
|
||||
|
||||
router = APIRouter(prefix="/detection", tags=["detection"])
|
||||
@@ -19,6 +20,16 @@ def list_detection_models() -> dict:
|
||||
return envelope({"models": [model.model_dump() for model in ModelRegistryService.list_model_capabilities()]})
|
||||
|
||||
|
||||
@router.get("/yolo/preflight", response_model=dict)
|
||||
def get_yolo_preflight(tile_manifest_path: str | None = None, check_model_load: bool = False) -> dict:
|
||||
return envelope(
|
||||
YoloPreflightService.run(
|
||||
tile_manifest_path=tile_manifest_path,
|
||||
check_model_load=check_model_load,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@router.post("/run", response_model=dict)
|
||||
def run_detection(payload: DetectionRunRequest, db: Session = Depends(get_db)) -> dict:
|
||||
result = DetectionService.run_detection(
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def test_detection_lab_surfaces_yolo_runtime_preflight() -> None:
|
||||
lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
hook = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8")
|
||||
api = (ROOT / "frontend" / "src" / "services" / "api" / "detection.ts").read_text(encoding="utf-8")
|
||||
types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8")
|
||||
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
|
||||
|
||||
assert "YOLO runtime preflight" in lab
|
||||
assert "torch_version" in lab
|
||||
assert "ultralytics_version" in lab
|
||||
assert "cuda_available" in lab
|
||||
assert "onRefreshYoloPreflight" in lab
|
||||
assert "loadYoloPreflight" in hook
|
||||
assert "getYoloPreflight" in api
|
||||
assert "/api/v1/detection/yolo/preflight" in api
|
||||
assert "interface YoloPreflightResponse" in types
|
||||
assert "yoloPreflight={yoloPreflight}" in app
|
||||
@@ -5,7 +5,10 @@ import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.core.config import Settings
|
||||
from app.main import app
|
||||
from app.services.yolo_preflight_service import YoloPreflightService
|
||||
|
||||
|
||||
@@ -213,3 +216,21 @@ def test_yolo_preflight_script_rejects_assumed_dependencies_for_model_load(tmp_p
|
||||
|
||||
assert result.returncode != 0
|
||||
assert "--check-model-load cannot be combined with --assume-dependencies" in result.stderr
|
||||
|
||||
|
||||
def test_yolo_preflight_api_returns_canonical_envelope(monkeypatch, tmp_path: Path) -> None:
|
||||
monkeypatch.setenv("YOLO_ENABLED", "false")
|
||||
monkeypatch.setenv("YOLO_MODEL_PATH", str(tmp_path / "missing.pt"))
|
||||
monkeypatch.setenv("YOLO_CONFIG_DIR", str(tmp_path / "ultralytics"))
|
||||
|
||||
response = TestClient(app).get("/api/v1/detection/yolo/preflight")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert set(payload) == {"data"}
|
||||
assert payload["data"]["status"] == "not_configured"
|
||||
assert payload["data"]["checks"]["enabled"] is False
|
||||
assert payload["data"]["runtime"]["model_directory"] == str(tmp_path)
|
||||
assert payload["data"]["runtime"]["yolo_config_dir"] == str(tmp_path / "ultralytics")
|
||||
assert payload["data"]["will_download_models"] is False
|
||||
assert payload["data"]["will_run_inference"] is False
|
||||
|
||||
Reference in New Issue
Block a user