|
| 1 | +@echo off |
| 2 | +REM deploy.bat — Zero-assumption bootstrapper for YOLO 2026 Detection Skill (Windows) |
| 3 | +REM |
| 4 | +REM Probes the system for Python, GPU backends, and installs the minimum |
| 5 | +REM viable stack. Called by Aegis skill-runtime-manager during installation. |
| 6 | +REM |
| 7 | +REM Uses skills\lib\env_config.py for hardware detection and model optimization. |
| 8 | +REM |
| 9 | +REM Exit codes: |
| 10 | +REM 0 = success |
| 11 | +REM 1 = fatal error (no Python found) |
| 12 | +REM 2 = partial success (CPU-only fallback) |
| 13 | + |
| 14 | +setlocal enabledelayedexpansion |
| 15 | + |
| 16 | +set "SKILL_DIR=%~dp0" |
| 17 | +REM Remove trailing backslash |
| 18 | +if "%SKILL_DIR:~-1%"=="\" set "SKILL_DIR=%SKILL_DIR:~0,-1%" |
| 19 | +set "VENV_DIR=%SKILL_DIR%\.venv" |
| 20 | +set "LOG_PREFIX=[YOLO-2026-deploy]" |
| 21 | + |
| 22 | +REM Resolve lib dir (two levels up + lib) |
| 23 | +set "LIB_DIR=" |
| 24 | +if exist "%SKILL_DIR%\..\..\lib\env_config.py" ( |
| 25 | + pushd "%SKILL_DIR%\..\..\lib" |
| 26 | + set "LIB_DIR=!CD!" |
| 27 | + popd |
| 28 | +) |
| 29 | + |
| 30 | +REM ─── Step 1: Find Python ─────────────────────────────────────────────────── |
| 31 | + |
| 32 | +echo %LOG_PREFIX% Searching for Python...>&2 |
| 33 | + |
| 34 | +set "PYTHON_CMD=" |
| 35 | + |
| 36 | +REM Try the Windows Python launcher (py.exe) first — ships with python.org installer |
| 37 | +for %%V in (3.12 3.11 3.10 3.9) do ( |
| 38 | + if not defined PYTHON_CMD ( |
| 39 | + py -%%V --version >nul 2>&1 |
| 40 | + if !errorlevel! equ 0 ( |
| 41 | + set "PYTHON_CMD=py -%%V" |
| 42 | + ) |
| 43 | + ) |
| 44 | +) |
| 45 | + |
| 46 | +REM Fallback: bare python3 / python on PATH |
| 47 | +if not defined PYTHON_CMD ( |
| 48 | + python3 --version >nul 2>&1 |
| 49 | + if !errorlevel! equ 0 ( |
| 50 | + REM Verify version >= 3.9 |
| 51 | + for /f "tokens=2 delims= " %%A in ('python3 --version 2^>^&1') do set "_pyver=%%A" |
| 52 | + for /f "tokens=1,2 delims=." %%A in ("!_pyver!") do ( |
| 53 | + if %%A geq 3 if %%B geq 9 set "PYTHON_CMD=python3" |
| 54 | + ) |
| 55 | + ) |
| 56 | +) |
| 57 | + |
| 58 | +if not defined PYTHON_CMD ( |
| 59 | + python --version >nul 2>&1 |
| 60 | + if !errorlevel! equ 0 ( |
| 61 | + for /f "tokens=2 delims= " %%A in ('python --version 2^>^&1') do set "_pyver=%%A" |
| 62 | + for /f "tokens=1,2 delims=." %%A in ("!_pyver!") do ( |
| 63 | + if %%A geq 3 if %%B geq 9 set "PYTHON_CMD=python" |
| 64 | + ) |
| 65 | + ) |
| 66 | +) |
| 67 | + |
| 68 | +if not defined PYTHON_CMD ( |
| 69 | + echo %LOG_PREFIX% ERROR: No Python ^>=3.9 found. Install Python 3.9+ and retry.>&2 |
| 70 | + echo {"event": "error", "stage": "python", "message": "No Python >=3.9 found"} |
| 71 | + exit /b 1 |
| 72 | +) |
| 73 | + |
| 74 | +for /f "tokens=*" %%A in ('!PYTHON_CMD! --version 2^>^&1') do set "PY_VERSION=%%A" |
| 75 | +echo %LOG_PREFIX% Using Python: %PYTHON_CMD% (%PY_VERSION%)>&2 |
| 76 | +echo {"event": "progress", "stage": "python", "message": "Found %PY_VERSION%"} |
| 77 | + |
| 78 | +REM ─── Step 2: Create virtual environment ──────────────────────────────────── |
| 79 | + |
| 80 | +if not exist "%VENV_DIR%\Scripts\python.exe" ( |
| 81 | + echo %LOG_PREFIX% Creating virtual environment...>&2 |
| 82 | + %PYTHON_CMD% -m venv "%VENV_DIR%" |
| 83 | + if !errorlevel! neq 0 ( |
| 84 | + echo %LOG_PREFIX% ERROR: Failed to create virtual environment>&2 |
| 85 | + echo {"event": "error", "stage": "venv", "message": "Failed to create venv"} |
| 86 | + exit /b 1 |
| 87 | + ) |
| 88 | +) |
| 89 | + |
| 90 | +set "PIP=%VENV_DIR%\Scripts\pip.exe" |
| 91 | +set "VPYTHON=%VENV_DIR%\Scripts\python.exe" |
| 92 | + |
| 93 | +"%PIP%" install --upgrade pip -q >nul 2>&1 |
| 94 | + |
| 95 | +echo {"event": "progress", "stage": "venv", "message": "Virtual environment ready"} |
| 96 | + |
| 97 | +REM ─── Step 2.5: Bundle env_config.py alongside detect.py ──────────────────── |
| 98 | + |
| 99 | +if defined LIB_DIR ( |
| 100 | + if exist "%LIB_DIR%\env_config.py" ( |
| 101 | + copy /Y "%LIB_DIR%\env_config.py" "%SKILL_DIR%\scripts\env_config.py" >nul 2>&1 |
| 102 | + echo %LOG_PREFIX% Bundled env_config.py into scripts\>&2 |
| 103 | + ) |
| 104 | +) |
| 105 | + |
| 106 | +REM ─── Step 3: Detect hardware via env_config ──────────────────────────────── |
| 107 | + |
| 108 | +set "BACKEND=cpu" |
| 109 | + |
| 110 | +REM Find env_config.py — bundled copy or repo lib\ |
| 111 | +set "ENV_CONFIG_DIR=" |
| 112 | +if exist "%SKILL_DIR%\scripts\env_config.py" ( |
| 113 | + set "ENV_CONFIG_DIR=%SKILL_DIR%\scripts" |
| 114 | +) else if defined LIB_DIR ( |
| 115 | + if exist "%LIB_DIR%\env_config.py" ( |
| 116 | + set "ENV_CONFIG_DIR=%LIB_DIR%" |
| 117 | + ) |
| 118 | +) |
| 119 | + |
| 120 | +if defined ENV_CONFIG_DIR ( |
| 121 | + echo %LOG_PREFIX% Detecting hardware via env_config.py...>&2 |
| 122 | + |
| 123 | + REM Run env_config detection via Python |
| 124 | + for /f "tokens=*" %%B in ('"%VPYTHON%" -c "import sys; sys.path.insert(0, r'!ENV_CONFIG_DIR!'); from env_config import HardwareEnv; env = HardwareEnv.detect(); print(env.backend)" 2^>nul') do ( |
| 125 | + set "DETECTED_BACKEND=%%B" |
| 126 | + ) |
| 127 | + |
| 128 | + REM Validate backend value (Windows: only cuda, intel, cpu are realistic) |
| 129 | + if "!DETECTED_BACKEND!"=="cuda" ( |
| 130 | + set "BACKEND=cuda" |
| 131 | + ) else if "!DETECTED_BACKEND!"=="intel" ( |
| 132 | + set "BACKEND=intel" |
| 133 | + ) else if "!DETECTED_BACKEND!"=="cpu" ( |
| 134 | + set "BACKEND=cpu" |
| 135 | + ) else ( |
| 136 | + echo %LOG_PREFIX% env_config returned '!DETECTED_BACKEND!', falling back to heuristic>&2 |
| 137 | + set "BACKEND=cpu" |
| 138 | + ) |
| 139 | + |
| 140 | + echo %LOG_PREFIX% env_config detected backend: !BACKEND!>&2 |
| 141 | +) else ( |
| 142 | + echo %LOG_PREFIX% env_config.py not found, using heuristic detection...>&2 |
| 143 | + |
| 144 | + REM Fallback: inline GPU detection via nvidia-smi |
| 145 | + where nvidia-smi >nul 2>&1 |
| 146 | + if !errorlevel! equ 0 ( |
| 147 | + for /f "tokens=*" %%G in ('nvidia-smi --query-gpu^=driver_version --format^=csv^,noheader 2^>nul') do ( |
| 148 | + if not "%%G"=="" ( |
| 149 | + set "BACKEND=cuda" |
| 150 | + echo %LOG_PREFIX% Detected NVIDIA GPU ^(driver: %%G^)>&2 |
| 151 | + ) |
| 152 | + ) |
| 153 | + ) |
| 154 | +) |
| 155 | + |
| 156 | +echo {"event": "progress", "stage": "gpu", "backend": "!BACKEND!", "message": "Compute backend: !BACKEND!"} |
| 157 | + |
| 158 | +REM ─── Step 4: Install requirements ────────────────────────────────────────── |
| 159 | + |
| 160 | +set "REQ_FILE=%SKILL_DIR%\requirements_!BACKEND!.txt" |
| 161 | + |
| 162 | +if not exist "!REQ_FILE!" ( |
| 163 | + echo %LOG_PREFIX% WARNING: !REQ_FILE! not found, falling back to CPU>&2 |
| 164 | + set "REQ_FILE=%SKILL_DIR%\requirements_cpu.txt" |
| 165 | + set "BACKEND=cpu" |
| 166 | +) |
| 167 | + |
| 168 | +echo %LOG_PREFIX% Installing dependencies from !REQ_FILE! ...>&2 |
| 169 | +echo {"event": "progress", "stage": "install", "message": "Installing !BACKEND! dependencies..."} |
| 170 | + |
| 171 | +if "!BACKEND!"=="cuda" ( |
| 172 | + REM CUDA on Windows: install torch with CUDA index, then remaining deps |
| 173 | + "%PIP%" install torch torchvision --index-url https://download.pytorch.org/whl/cu124 -q 2>&1 | findstr /V "^$" >nul |
| 174 | + if !errorlevel! neq 0 ( |
| 175 | + echo %LOG_PREFIX% WARNING: CUDA torch install failed, trying cu121...>&2 |
| 176 | + "%PIP%" install torch torchvision --index-url https://download.pytorch.org/whl/cu121 -q 2>&1 | findstr /V "^$" >nul |
| 177 | + ) |
| 178 | + REM Install remaining requirements (ultralytics, etc.) |
| 179 | + "%PIP%" install -r "!REQ_FILE!" -q 2>&1 | findstr /V "^$" >nul |
| 180 | +) else ( |
| 181 | + "%PIP%" install -r "!REQ_FILE!" -q 2>&1 | findstr /V "^$" >nul |
| 182 | +) |
| 183 | + |
| 184 | +REM ─── Step 5: Pre-convert model to optimized format ───────────────────────── |
| 185 | + |
| 186 | +if "!BACKEND!" neq "cpu" ( |
| 187 | + echo %LOG_PREFIX% Pre-converting model to optimized format for !BACKEND!...>&2 |
| 188 | + echo {"event": "progress", "stage": "optimize", "message": "Converting model for !BACKEND! (~30-120s)..."} |
| 189 | + |
| 190 | + "%VPYTHON%" -c "import sys; sys.path.insert(0, r'!ENV_CONFIG_DIR!'); from env_config import HardwareEnv; env = HardwareEnv.detect(); from ultralytics import YOLO; model = YOLO('yolo26n.pt'); result = env.export_model(model, 'yolo26n'); print(f'Optimized model exported: {result}' if result else 'Export skipped or failed')" 2>&1 |
| 191 | + |
| 192 | + if !errorlevel! equ 0 ( |
| 193 | + echo {"event": "progress", "stage": "optimize", "message": "Model optimization complete"} |
| 194 | + ) else ( |
| 195 | + echo %LOG_PREFIX% WARNING: Model optimization failed, will use PyTorch at runtime>&2 |
| 196 | + echo {"event": "progress", "stage": "optimize", "message": "Optimization failed — PyTorch fallback"} |
| 197 | + ) |
| 198 | +) else if exist "%SKILL_DIR%\requirements_cpu.txt" ( |
| 199 | + echo %LOG_PREFIX% Pre-converting model to ONNX for CPU...>&2 |
| 200 | + echo {"event": "progress", "stage": "optimize", "message": "Converting model for cpu (~30-120s)..."} |
| 201 | + |
| 202 | + "%VPYTHON%" -c "import sys; sys.path.insert(0, r'!ENV_CONFIG_DIR!'); from env_config import HardwareEnv; env = HardwareEnv.detect(); from ultralytics import YOLO; model = YOLO('yolo26n.pt'); result = env.export_model(model, 'yolo26n'); print(f'Optimized model exported: {result}' if result else 'Export skipped or failed')" 2>&1 |
| 203 | + |
| 204 | + if !errorlevel! equ 0 ( |
| 205 | + echo {"event": "progress", "stage": "optimize", "message": "Model optimization complete"} |
| 206 | + ) else ( |
| 207 | + echo %LOG_PREFIX% WARNING: Model optimization failed, will use PyTorch at runtime>&2 |
| 208 | + echo {"event": "progress", "stage": "optimize", "message": "Optimization failed — PyTorch fallback"} |
| 209 | + ) |
| 210 | +) |
| 211 | + |
| 212 | +REM ─── Step 6: Verify installation ─────────────────────────────────────────── |
| 213 | + |
| 214 | +echo %LOG_PREFIX% Verifying installation...>&2 |
| 215 | +"%VPYTHON%" -c "import sys, json; sys.path.insert(0, r'!ENV_CONFIG_DIR!'); from env_config import HardwareEnv; env = HardwareEnv.detect(); print(json.dumps(env.to_dict(), indent=2))" 2>&1 |
| 216 | + |
| 217 | +echo {"event": "complete", "backend": "!BACKEND!", "message": "YOLO 2026 skill installed (!BACKEND! backend)"} |
| 218 | +echo %LOG_PREFIX% Done! Backend: !BACKEND!>&2 |
| 219 | + |
| 220 | +endlocal |
| 221 | +exit /b 0 |
0 commit comments