Surface YOLO preflight in Detection Lab
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
- 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 YOLO preflight runtime diagnostics for dependency assumption state, model directory, `YOLO_CONFIG_DIR`, installed `torch`/`ultralytics` versions and CUDA availability without running inference or downloading weights.
|
||||
- Added a canonical `GET /api/v1/detection/yolo/preflight` endpoint and Detection Lab panel so operators can inspect live YOLO runtime readiness from the web UI.
|
||||
- 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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -636,6 +636,52 @@ Returns object-detection model capability descriptors.
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/api/v1/detection/yolo/preflight`
|
||||
|
||||
Returns a canonical envelope with read-only configured-YOLO runtime preflight
|
||||
state. Optional query parameters:
|
||||
|
||||
- `tile_manifest_path`: existing raster tile manifest path to validate.
|
||||
- `check_model_load`: default `false`; when `true`, explicitly loads only the
|
||||
configured local model file for compatibility smoke. It never downloads
|
||||
weights and never runs inference.
|
||||
|
||||
Response data:
|
||||
|
||||
```json
|
||||
{
|
||||
"model_id": "yolo-configured",
|
||||
"model_path": null,
|
||||
"tile_manifest_path": null,
|
||||
"status": "not_configured",
|
||||
"message": "YOLO_MODEL_PATH is not set. GeoIntel will not download model weights automatically.",
|
||||
"checks": {
|
||||
"enabled": true,
|
||||
"dependencies_available": true,
|
||||
"model_path_set": false,
|
||||
"model_file_exists": null,
|
||||
"model_load_requested": false,
|
||||
"model_load_ok": null,
|
||||
"manifest_path_set": null,
|
||||
"manifest_valid": null,
|
||||
"tile_paths_exist": null,
|
||||
"tile_limit_ok": null
|
||||
},
|
||||
"runtime": {
|
||||
"dependencies_assumed": false,
|
||||
"model_directory": null,
|
||||
"yolo_config_dir": "/app/storage/ultralytics",
|
||||
"torch_version": "2.12.1",
|
||||
"ultralytics_version": "8.4.88",
|
||||
"cuda_available": false
|
||||
},
|
||||
"tile_count": 0,
|
||||
"max_tiles": 100,
|
||||
"will_download_models": false,
|
||||
"will_run_inference": false
|
||||
}
|
||||
```
|
||||
|
||||
### POST `/api/v1/detection/run`
|
||||
|
||||
Creates a detection job and detection analysis run. If the requested model is unavailable, the job and analysis run are marked `failed` with `DETECTION_MODEL_UNAVAILABLE` or `DETECTION_DEPENDENCY_UNAVAILABLE`.
|
||||
|
||||
@@ -8,6 +8,7 @@ Changed:
|
||||
- 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.
|
||||
- Added YOLO preflight runtime diagnostics so operators can see dependency assumption state, model directory, `YOLO_CONFIG_DIR`, installed `torch`/`ultralytics` package versions and CUDA availability without loading a model, running inference or downloading weights.
|
||||
- Added a read-only `GET /api/v1/detection/yolo/preflight` endpoint and Detection Lab YOLO runtime preflight panel so browser operators can inspect live AI runtime readiness without loading a model, running inference or downloading weights.
|
||||
- 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`.
|
||||
|
||||
@@ -54,6 +55,17 @@ Validation:
|
||||
- Deploy-time live migration smoke passed on Tower; PostGIS reported `3.6 USE_GEOS=1 USE_PROJ=1 USE_STATS=1` and Alembic head was `202606120900`.
|
||||
- Deploy-time browser runtime verification passed for `http://192.168.10.150:1202`, API proxy and icon.
|
||||
- Tower container check passed: remote checkout is `7a29e78`, `geointel` is healthy on `0.0.0.0:1202->80/tcp`, and `scripts/yolo_preflight.py --enabled --json` reports `dependencies_available=true`, `torch_version=2.12.1`, `ultralytics_version=8.4.88`, `cuda_available=false`, `yolo_config_dir=/app/storage/ultralytics`, `status=not_configured`, `will_download_models=false` and `will_run_inference=false`.
|
||||
- RED: `python -m pytest backend\tests\test_sprint13_yolo_preflight.py::test_yolo_preflight_api_returns_canonical_envelope backend\tests\test_sprint118_yolo_preflight_ui.py -q` failed before implementation because `/api/v1/detection/yolo/preflight` returned 404 and the Detection Lab did not surface a YOLO runtime preflight panel.
|
||||
- `python -m pytest backend\tests\test_sprint13_yolo_preflight.py::test_yolo_preflight_api_returns_canonical_envelope backend\tests\test_sprint118_yolo_preflight_ui.py -q` passed: 2 tests.
|
||||
- `cd frontend && npm run typecheck` passed after adding the preflight API client and Detection Lab panel.
|
||||
- `python -m pytest backend\tests\test_sprint13_yolo_preflight.py backend\tests\test_sprint118_yolo_preflight_ui.py backend\tests\test_sprint48_api_contract_audit.py -q` passed: 13 tests.
|
||||
- `python -m compileall backend/app` passed.
|
||||
- `cd backend && python -m pytest -q` passed: 371 tests.
|
||||
- `cd frontend && npm run build` passed.
|
||||
- `bash scripts/run_readiness_check.sh` passed: 371 backend tests plus frontend typecheck/build and API contract audit for 80 documented routes.
|
||||
- `cd backend && python -m alembic heads` passed: `202606120900 (head)`.
|
||||
- `cd backend && python -m alembic upgrade head --sql` passed.
|
||||
- `bash -n scripts/live_migration_smoke.sh` and `bash -n scripts/deploy_tower.sh` passed.
|
||||
|
||||
Limitations:
|
||||
- `GEOINTEL_INSTALL_AI=true` installs optional PyTorch/Ultralytics dependencies but still requires a user-provided local model file; GeoIntel does not download weights.
|
||||
|
||||
@@ -80,6 +80,7 @@ This file now starts with the current implementation status. Older preparation/b
|
||||
- [x] Add basemap policy notice and guided GIS query-to-QA/export workflow in the Map workspace.
|
||||
- [x] Add reusable latest-result mode for repeated Map QA/QC runs without duplicate derived artifacts.
|
||||
- [x] Add opt-in Docker/Unraid AI build/runtime path for local PyTorch/Ultralytics YOLO operation.
|
||||
- [x] Surface configured-YOLO runtime preflight status through the API and Detection Lab UI.
|
||||
- [x] Add one-click full GIS workflow action for query, derived dataset, QA/QC and export handoff.
|
||||
- [x] Add QA/QC workspace result hierarchy and filter density polish.
|
||||
- [x] Add Change Detection panel hierarchy and analysis workspace density polish.
|
||||
|
||||
@@ -120,6 +120,7 @@ AI Lab run controls explicitly explain when no raster dataset is available, inst
|
||||
## Sprint 8B additions
|
||||
- Detection Lab now exposes the `yolo-configured` capability reported by the backend.
|
||||
- When `yolo-configured` is selected, users can provide an existing raster tile manifest path.
|
||||
- Detection Lab includes a read-only YOLO runtime preflight panel with backend status, dependency visibility, local model configuration, `torch`/`ultralytics` versions, CUDA state and `YOLO_CONFIG_DIR`.
|
||||
- The UI still does not download models or create fake detections; backend status and error codes remain the source of truth.
|
||||
|
||||
## Sprint 8C additions
|
||||
|
||||
@@ -242,7 +242,11 @@ function App(): JSX.Element {
|
||||
detectionQaResult,
|
||||
detectionQaError,
|
||||
runningDetectionQa,
|
||||
yoloPreflight,
|
||||
loadingYoloPreflight,
|
||||
yoloPreflightError,
|
||||
loadDetectionModels,
|
||||
loadYoloPreflight,
|
||||
loadDetectionRuns,
|
||||
loadDetectionResults,
|
||||
runDetection,
|
||||
@@ -945,10 +949,14 @@ function App(): JSX.Element {
|
||||
detectionQaResult={detectionQaResult}
|
||||
detectionQaError={detectionQaError}
|
||||
runningDetectionQa={runningDetectionQa}
|
||||
yoloPreflight={yoloPreflight}
|
||||
loadingYoloPreflight={loadingYoloPreflight}
|
||||
yoloPreflightError={yoloPreflightError}
|
||||
selectedProjectId={selectedProjectId}
|
||||
rasterDatasets={rasterDatasets}
|
||||
referenceDatasets={referenceDatasets}
|
||||
onLoadModels={loadDetectionModels}
|
||||
onRefreshYoloPreflight={() => loadYoloPreflight()}
|
||||
onSelectDataset={setSelectedDetectionDatasetId}
|
||||
onSelectModel={setSelectedDetectionModelId}
|
||||
onSetConfidenceThreshold={setDetectionConfidenceThreshold}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
DetectionRead,
|
||||
DetectionRunRead,
|
||||
DetectionRunResponse,
|
||||
YoloPreflightResponse,
|
||||
} from '../../types'
|
||||
|
||||
interface DetectionLabProps {
|
||||
@@ -28,10 +29,14 @@ interface DetectionLabProps {
|
||||
detectionQaResult: DetectionQaResult | null
|
||||
detectionQaError: string | null
|
||||
runningDetectionQa: boolean
|
||||
yoloPreflight: YoloPreflightResponse | null
|
||||
loadingYoloPreflight: boolean
|
||||
yoloPreflightError: string | null
|
||||
selectedProjectId: string | null
|
||||
rasterDatasets: DatasetCreateResponse[]
|
||||
referenceDatasets: DatasetCreateResponse[]
|
||||
onLoadModels: () => void
|
||||
onRefreshYoloPreflight: () => void
|
||||
onSelectDataset: (datasetId: string) => void
|
||||
onSelectModel: (modelId: string) => void
|
||||
onSetConfidenceThreshold: (value: number) => void
|
||||
@@ -67,10 +72,14 @@ export function DetectionLab({
|
||||
detectionQaResult,
|
||||
detectionQaError,
|
||||
runningDetectionQa,
|
||||
yoloPreflight,
|
||||
loadingYoloPreflight,
|
||||
yoloPreflightError,
|
||||
selectedProjectId,
|
||||
rasterDatasets,
|
||||
referenceDatasets,
|
||||
onLoadModels,
|
||||
onRefreshYoloPreflight,
|
||||
onSelectDataset,
|
||||
onSelectModel,
|
||||
onSetConfidenceThreshold,
|
||||
@@ -164,6 +173,76 @@ export function DetectionLab({
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="ai-lab-model-surface" aria-label="YOLO runtime preflight">
|
||||
<div className="ai-lab-section-header">
|
||||
<div>
|
||||
<h3>YOLO runtime preflight</h3>
|
||||
<p>Read-only runtime status. This does not load a model, run inference or download weights.</p>
|
||||
</div>
|
||||
<button className="secondary-action" type="button" onClick={onRefreshYoloPreflight} disabled={loadingYoloPreflight}>
|
||||
Refresh preflight
|
||||
</button>
|
||||
</div>
|
||||
<div className="ai-lab-state-stack">
|
||||
{loadingYoloPreflight ? (
|
||||
<div className="result-state result-state-loading">
|
||||
<strong>Loading YOLO preflight.</strong>
|
||||
<p>Checking backend runtime configuration and optional dependency visibility.</p>
|
||||
</div>
|
||||
) : null}
|
||||
{yoloPreflightError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>YOLO preflight unavailable.</strong>
|
||||
<p>{yoloPreflightError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{!yoloPreflight && !loadingYoloPreflight && !yoloPreflightError ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No YOLO preflight loaded.</strong>
|
||||
<p>Refresh preflight to inspect the live backend AI runtime before running configured YOLO.</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{yoloPreflight ? (
|
||||
<div className={yoloPreflight.status === 'ready' ? 'lab-readiness-panel lab-readiness-panel-ready' : 'lab-readiness-panel'}>
|
||||
<div className="ai-lab-section-header">
|
||||
<div>
|
||||
<h3>Status: {yoloPreflight.status}</h3>
|
||||
<p>{yoloPreflight.message}</p>
|
||||
</div>
|
||||
<span className={yoloPreflight.status === 'ready' ? 'status-badge status-badge-ready' : 'status-badge'}>
|
||||
{yoloPreflight.checks.dependencies_available ? 'dependencies visible' : 'not ready'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="lab-readiness-grid">
|
||||
<div className={yoloPreflight.checks.enabled ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>YOLO enabled</span>
|
||||
<strong>{yoloPreflight.checks.enabled ? 'true' : 'false'}</strong>
|
||||
</div>
|
||||
<div className={yoloPreflight.checks.dependencies_available ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Dependencies</span>
|
||||
<strong>{yoloPreflight.checks.dependencies_available === true ? 'available' : yoloPreflight.checks.dependencies_available === false ? 'unavailable' : 'not checked'}</strong>
|
||||
</div>
|
||||
<div className={yoloPreflight.checks.model_file_exists ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Local model file</span>
|
||||
<strong>{yoloPreflight.checks.model_file_exists === true ? 'found' : yoloPreflight.checks.model_path_set ? 'missing' : 'not configured'}</strong>
|
||||
</div>
|
||||
<div className={yoloPreflight.runtime.cuda_available ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>CUDA</span>
|
||||
<strong>{yoloPreflight.runtime.cuda_available === true ? 'available' : yoloPreflight.runtime.cuda_available === false ? 'not available' : 'not checked'}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="entity-meta">
|
||||
<span>torch_version: {yoloPreflight.runtime.torch_version ?? 'n/a'}</span>
|
||||
<span>ultralytics_version: {yoloPreflight.runtime.ultralytics_version ?? 'n/a'}</span>
|
||||
<span>cuda_available: {String(yoloPreflight.runtime.cuda_available ?? 'unknown')}</span>
|
||||
<span>YOLO_CONFIG_DIR: {yoloPreflight.runtime.yolo_config_dir ?? 'n/a'}</span>
|
||||
<span>model directory: {yoloPreflight.runtime.model_directory ?? 'n/a'}</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="lab-block">
|
||||
<div className="ai-lab-run-surface" aria-label="Detection run controls">
|
||||
<h3>Run detection</h3>
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
DetectionRunRead,
|
||||
DetectionRunResponse,
|
||||
QualityCheckRead,
|
||||
YoloPreflightResponse,
|
||||
} from '../types'
|
||||
import { formatError } from '../lib/formatError'
|
||||
|
||||
@@ -47,6 +48,9 @@ export function useDetectionWorkflow({
|
||||
const [detectionQaResult, setDetectionQaResult] = useState<DetectionQaResult | null>(null)
|
||||
const [detectionQaError, setDetectionQaError] = useState<string | null>(null)
|
||||
const [runningDetectionQa, setRunningDetectionQa] = useState(false)
|
||||
const [yoloPreflight, setYoloPreflight] = useState<YoloPreflightResponse | null>(null)
|
||||
const [loadingYoloPreflight, setLoadingYoloPreflight] = useState(false)
|
||||
const [yoloPreflightError, setYoloPreflightError] = useState<string | null>(null)
|
||||
|
||||
const loadDetectionModels = async () => {
|
||||
setLoadingDetectionModels(true)
|
||||
@@ -64,6 +68,21 @@ export function useDetectionWorkflow({
|
||||
}
|
||||
}
|
||||
|
||||
const loadYoloPreflight = async (tileManifestPath = detectionTileManifestPath) => {
|
||||
setLoadingYoloPreflight(true)
|
||||
setYoloPreflightError(null)
|
||||
try {
|
||||
const response = await detectionApi.getYoloPreflight({
|
||||
tile_manifest_path: tileManifestPath.trim() || null,
|
||||
})
|
||||
setYoloPreflight(response)
|
||||
} catch (error) {
|
||||
setYoloPreflightError(formatError(error, 'Failed to load YOLO preflight status'))
|
||||
} finally {
|
||||
setLoadingYoloPreflight(false)
|
||||
}
|
||||
}
|
||||
|
||||
const loadDetectionRuns = async (projectId = selectedProjectId) => {
|
||||
if (!projectId) {
|
||||
setDetectionRuns([])
|
||||
@@ -199,7 +218,11 @@ export function useDetectionWorkflow({
|
||||
detectionQaResult,
|
||||
detectionQaError,
|
||||
runningDetectionQa,
|
||||
yoloPreflight,
|
||||
loadingYoloPreflight,
|
||||
yoloPreflightError,
|
||||
loadDetectionModels,
|
||||
loadYoloPreflight,
|
||||
loadDetectionRuns,
|
||||
loadDetectionResults,
|
||||
runDetection,
|
||||
|
||||
@@ -8,9 +8,10 @@ import type {
|
||||
DetectionRunRead,
|
||||
DetectionRunRequest,
|
||||
DetectionRunResponse,
|
||||
YoloPreflightResponse,
|
||||
} from '../../types'
|
||||
|
||||
function queryString(params: Record<string, string | number | null | undefined>): string {
|
||||
function queryString(params: Record<string, string | number | boolean | null | undefined>): string {
|
||||
const searchParams = new URLSearchParams()
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
@@ -23,6 +24,8 @@ function queryString(params: Record<string, string | number | null | undefined>)
|
||||
|
||||
export const detectionApi = {
|
||||
listModels: (): Promise<DetectionModelsResponse> => apiGet<DetectionModelsResponse>('/api/v1/detection/models'),
|
||||
getYoloPreflight: (params: { tile_manifest_path?: string | null; check_model_load?: boolean | null } = {}): Promise<YoloPreflightResponse> =>
|
||||
apiGet<YoloPreflightResponse>(`/api/v1/detection/yolo/preflight${queryString(params)}`),
|
||||
run: (payload: DetectionRunRequest): Promise<DetectionRunResponse> =>
|
||||
apiPost<DetectionRunResponse>('/api/v1/detection/run', payload),
|
||||
listRuns: (params: { project_id?: string | null; dataset_id?: string | null } = {}): Promise<DetectionRunListResponse> =>
|
||||
|
||||
@@ -404,6 +404,43 @@ export interface DetectionModelsResponse {
|
||||
models: DetectionModelCapability[]
|
||||
}
|
||||
|
||||
export interface YoloPreflightChecks {
|
||||
enabled: boolean
|
||||
dependencies_available?: boolean | null
|
||||
model_path_set?: boolean | null
|
||||
model_file_exists?: boolean | null
|
||||
model_load_requested: boolean
|
||||
model_load_ok?: boolean | null
|
||||
manifest_path_set?: boolean | null
|
||||
manifest_valid?: boolean | null
|
||||
tile_paths_exist?: boolean | null
|
||||
tile_limit_ok?: boolean | null
|
||||
}
|
||||
|
||||
export interface YoloPreflightRuntime {
|
||||
dependencies_assumed: boolean
|
||||
model_directory?: string | null
|
||||
yolo_config_dir?: string | null
|
||||
torch_version?: string | null
|
||||
ultralytics_version?: string | null
|
||||
cuda_available?: boolean | null
|
||||
}
|
||||
|
||||
export interface YoloPreflightResponse {
|
||||
model_id: string
|
||||
model_path?: string | null
|
||||
tile_manifest_path?: string | null
|
||||
status: string
|
||||
message: string
|
||||
checks: YoloPreflightChecks
|
||||
runtime: YoloPreflightRuntime
|
||||
tile_count: number
|
||||
max_tiles: number
|
||||
will_download_models: boolean
|
||||
will_run_inference: boolean
|
||||
error_code?: string | null
|
||||
}
|
||||
|
||||
export interface DetectionRunRequest {
|
||||
project_id: string
|
||||
dataset_id: string
|
||||
|
||||
Reference in New Issue
Block a user