Skip to content

Commit a37e2b7

Browse files
committed
feat: shared hardware environment layer (env_config.py) for multi-backend inference
New shared library at skills/lib/env_config.py: - HardwareEnv.detect(): auto-detect CUDA, ROCm, MPS, Intel, CPU - export_model(): auto-export to TensorRT/ONNX/CoreML/OpenVINO - load_optimized(): load cached model with PyTorch fallback detect.py: replaced inline CoreML logic with HardwareEnv (2 lines) deploy.sh: uses env_config for hardware detection + generalized pre-conversion New: requirements_intel.txt (OpenVINO) Updated: requirements_cuda (tensorrt), rocm (onnxruntime-rocm), cpu (onnxruntime)
1 parent b8e7304 commit a37e2b7

8 files changed

Lines changed: 540 additions & 165 deletions

File tree

‎skills/detection/yolo-detection-2026/deploy.sh‎

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# Probes the system for Python, GPU backends, and installs the minimum
55
# viable stack. Called by Aegis skill-runtime-manager during installation.
66
#
7+
# Uses skills/lib/env_config.py for hardware detection and model optimization.
8+
#
79
# Exit codes:
810
# 0 = success
911
# 1 = fatal error (no Python found and cannot install)
@@ -13,6 +15,7 @@ set -euo pipefail
1315

1416
SKILL_DIR="$(cd "$(dirname "$0")" && pwd)"
1517
VENV_DIR="$SKILL_DIR/.venv"
18+
LIB_DIR="$(cd "$SKILL_DIR/../../lib" 2>/dev/null && pwd || echo "")"
1619
LOG_PREFIX="[YOLO-2026-deploy]"
1720

1821
log() { echo "$LOG_PREFIX $*" >&2; }
@@ -21,7 +24,6 @@ emit() { echo "$1"; } # JSON to stdout for Aegis to parse
2124
# ─── Step 1: Find or install Python ─────────────────────────────────────────
2225

2326
find_python() {
24-
# Check common Python 3 locations
2527
for cmd in python3.12 python3.11 python3.10 python3.9 python3; do
2628
if command -v "$cmd" &>/dev/null; then
2729
local ver
@@ -36,7 +38,6 @@ find_python() {
3638
fi
3739
done
3840

39-
# Check conda
4041
if command -v conda &>/dev/null; then
4142
log "No system Python >=3.9 found, but conda is available"
4243
log "Creating conda environment..."
@@ -48,7 +49,6 @@ find_python() {
4849
return 0
4950
fi
5051

51-
# Check pyenv
5252
if command -v pyenv &>/dev/null; then
5353
log "No system Python >=3.9 found, using pyenv..."
5454
pyenv install -s 3.11.9
@@ -76,55 +76,60 @@ if [ ! -d "$VENV_DIR" ]; then
7676
"$PYTHON_CMD" -m venv "$VENV_DIR"
7777
fi
7878

79-
# Activate venv
8079
# shellcheck disable=SC1091
8180
source "$VENV_DIR/bin/activate"
8281
PIP="$VENV_DIR/bin/pip"
8382

84-
# Upgrade pip
8583
"$PIP" install --upgrade pip -q 2>/dev/null || true
8684

8785
emit '{"event": "progress", "stage": "venv", "message": "Virtual environment ready"}'
8886

89-
# ─── Step 3: Detect compute backend ─────────────────────────────────────────
87+
# ─── Step 3: Detect hardware via env_config ─────────────────────────────────
9088

9189
BACKEND="cpu"
9290

93-
detect_gpu() {
94-
# NVIDIA CUDA
91+
if [ -n "$LIB_DIR" ] && [ -f "$LIB_DIR/env_config.py" ]; then
92+
log "Detecting hardware via env_config.py..."
93+
DETECT_OUTPUT=$("$VENV_DIR/bin/python" -c "
94+
import sys
95+
sys.path.insert(0, '$LIB_DIR')
96+
from env_config import HardwareEnv
97+
env = HardwareEnv.detect()
98+
print(env.backend)
99+
" 2>&1) || true
100+
101+
# The last line of output is the backend name
102+
BACKEND=$(echo "$DETECT_OUTPUT" | tail -1)
103+
104+
# Validate backend value
105+
case "$BACKEND" in
106+
cuda|rocm|mps|intel|cpu) ;;
107+
*)
108+
log "env_config returned unexpected backend '$BACKEND', falling back to heuristic"
109+
BACKEND="cpu"
110+
;;
111+
esac
112+
113+
log "env_config detected backend: $BACKEND"
114+
else
115+
log "env_config.py not found, using heuristic detection..."
116+
117+
# Fallback: inline GPU detection (same as before)
95118
if command -v nvidia-smi &>/dev/null; then
96-
local cuda_ver
97119
cuda_ver=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1)
98120
if [ -n "$cuda_ver" ]; then
99121
BACKEND="cuda"
100122
log "Detected NVIDIA GPU (driver: $cuda_ver)"
101-
return 0
102123
fi
103-
fi
104-
105-
# AMD ROCm
106-
if command -v rocm-smi &>/dev/null || [ -d "/opt/rocm" ]; then
124+
elif command -v rocm-smi &>/dev/null || [ -d "/opt/rocm" ]; then
107125
BACKEND="rocm"
108126
log "Detected AMD ROCm"
109-
return 0
110-
fi
111-
112-
# Apple Silicon MPS
113-
if [ "$(uname)" = "Darwin" ]; then
114-
local arch
115-
arch=$(uname -m)
116-
if [ "$arch" = "arm64" ]; then
117-
BACKEND="mps"
118-
log "Detected Apple Silicon (MPS)"
119-
return 0
120-
fi
127+
elif [ "$(uname)" = "Darwin" ] && [ "$(uname -m)" = "arm64" ]; then
128+
BACKEND="mps"
129+
log "Detected Apple Silicon (MPS)"
121130
fi
131+
fi
122132

123-
log "No GPU detected, using CPU backend"
124-
return 0
125-
}
126-
127-
detect_gpu
128133
emit "{\"event\": \"progress\", \"stage\": \"gpu\", \"backend\": \"$BACKEND\", \"message\": \"Compute backend: $BACKEND\"}"
129134

130135
# ─── Step 4: Install requirements ────────────────────────────────────────────
@@ -142,39 +147,50 @@ emit "{\"event\": \"progress\", \"stage\": \"install\", \"message\": \"Installin
142147

143148
"$PIP" install -r "$REQ_FILE" -q 2>&1 | tail -5 >&2
144149

145-
# ─── Step 5: CoreML pre-conversion (MPS only) ───────────────────────────────
150+
# ─── Step 5: Pre-convert model to optimized format ───────────────────────────
146151

147-
if [ "$BACKEND" = "mps" ]; then
148-
log "Pre-converting default model to CoreML for ANE acceleration..."
149-
emit '{"event": "progress", "stage": "coreml", "message": "Converting model to CoreML (~30s)..."}'
152+
if [ "$BACKEND" != "cpu" ] || [ -f "$SKILL_DIR/requirements_cpu.txt" ]; then
153+
log "Pre-converting model to optimized format for $BACKEND..."
154+
emit "{\"event\": \"progress\", \"stage\": \"optimize\", \"message\": \"Converting model for $BACKEND (~30-120s)...\"}"
150155

151156
"$VENV_DIR/bin/python" -c "
152-
from ultralytics import YOLO
153-
model = YOLO('yolo26n.pt')
154-
exported = model.export(format='coreml', half=True, nms=False)
155-
print(f'CoreML model exported: {exported}')
157+
import sys
158+
sys.path.insert(0, '$LIB_DIR')
159+
from env_config import HardwareEnv
160+
env = HardwareEnv.detect()
161+
162+
if env.framework_ok:
163+
from ultralytics import YOLO
164+
model = YOLO('yolo26n.pt')
165+
result = env.export_model(model, 'yolo26n')
166+
if result:
167+
print(f'Optimized model exported: {result}')
168+
else:
169+
print('Export skipped or failed — will use PyTorch at runtime')
170+
else:
171+
print(f'Optimized runtime not available for {env.backend} — will use PyTorch')
156172
" 2>&1 | while read -r line; do log "$line"; done
157173

158174
if [ $? -eq 0 ]; then
159-
emit '{"event": "progress", "stage": "coreml", "message": "CoreML conversion complete"}'
175+
emit "{\"event\": \"progress\", \"stage\": \"optimize\", \"message\": \"Model optimization complete\"}"
160176
else
161-
log "WARNING: CoreML conversion failed, will use PyTorch MPS at runtime"
162-
emit '{"event": "progress", "stage": "coreml", "message": "CoreML conversion failed — PyTorch MPS fallback"}'
177+
log "WARNING: Model optimization failed, will use PyTorch at runtime"
178+
emit "{\"event\": \"progress\", \"stage\": \"optimize\", \"message\": \"Optimization failed — PyTorch fallback\"}"
163179
fi
164180
fi
165181

166182
# ─── Step 6: Verify installation ────────────────────────────────────────────
167183

168184
log "Verifying installation..."
169185
"$VENV_DIR/bin/python" -c "
170-
from ultralytics import YOLO
171-
import torch
172-
device = 'cpu'
173-
if torch.cuda.is_available(): device = 'cuda'
174-
elif hasattr(torch.backends, 'mps') and torch.backends.mps.is_available(): device = 'mps'
175-
print(f'OK: ultralytics loaded, torch device={device}')
186+
import sys
187+
sys.path.insert(0, '$LIB_DIR')
188+
from env_config import HardwareEnv
189+
import json
190+
191+
env = HardwareEnv.detect()
192+
print(json.dumps(env.to_dict(), indent=2))
176193
" 2>&1 | while read -r line; do log "$line"; done
177194

178195
emit "{\"event\": \"complete\", \"backend\": \"$BACKEND\", \"message\": \"YOLO 2026 skill installed ($BACKEND backend)\"}"
179196
log "Done! Backend: $BACKEND"
180-

‎skills/detection/yolo-detection-2026/requirements_cpu.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
torch>=2.4.0
55
torchvision>=0.19.0
66
ultralytics>=8.3.0
7+
onnxruntime>=1.18
78
numpy>=1.24.0
89
opencv-python-headless>=4.8.0
910
Pillow>=10.0.0
11+

‎skills/detection/yolo-detection-2026/requirements_cuda.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
torch>=2.4.0
55
torchvision>=0.19.0
66
ultralytics>=8.3.0
7+
tensorrt>=10.0
78
numpy>=1.24.0
89
opencv-python-headless>=4.8.0
910
Pillow>=10.0.0
11+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# YOLO 2026 — Intel (OpenVINO) requirements
2+
# Supports Intel CPUs, iGPUs, and NPUs (Meteor Lake+)
3+
torch>=2.4.0
4+
torchvision>=0.19.0
5+
ultralytics>=8.3.0
6+
openvino>=2024.0
7+
numpy>=1.24.0
8+
opencv-python-headless>=4.8.0
9+
Pillow>=10.0.0

‎skills/detection/yolo-detection-2026/requirements_rocm.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
torch>=2.4.0
55
torchvision>=0.19.0
66
ultralytics>=8.3.0
7+
onnxruntime-rocm>=1.18
78
numpy>=1.24.0
89
opencv-python-headless>=4.8.0
910
Pillow>=10.0.0
11+

0 commit comments

Comments
 (0)