Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions backend/app/utils/hardware_detect.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from __future__ import annotations

import shutil
import subprocess
import platform

import onnxruntime as ort
import psutil
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -35,11 +38,60 @@ def detect_physical_gpu() -> list[str]:
return gpu_names


def detect_apple_silicon() -> str | None:
if platform.system() != "Darwin":
return None
if platform.machine() != "arm64":
return None

sysctl = shutil.which("sysctl")
if sysctl:
try:
result = subprocess.run(
[sysctl, "-n", "machdep.cpu.brand_string"],
capture_output=True,
text=True,
check=True,
timeout=5,
)
chip_name = result.stdout.strip()
if chip_name:
return chip_name
except (OSError, subprocess.SubprocessError):
pass

proc = platform.processor()
if proc:
return proc

return "Apple Silicon"


def detect_apple_silicon_tier() -> str | None:
chip = detect_apple_silicon()
if chip is None:
return None

chip_lower = chip.lower()

# Base M1 and M2 chips (without Pro/Max/Ultra modifiers) are "small"
if any(base in chip_lower for base in ["m1", "m2"]):
if not any(mod in chip_lower for mod in ["pro", "max", "ultra"]):
return "small"

# Default to medium for all other Apple Silicon (Pro/Max/Ultra, M3+, and future chips)
return "medium"
Comment thread
coderabbitai[bot] marked this conversation as resolved.


def detect_hardware_tier() -> str:
"""
Detect system hardware to recommend the best YOLO/FaceNet model tier.
Returns: 'nano', 'small', or 'medium'
"""
apple_tier = detect_apple_silicon_tier()
if apple_tier is not None:
return apple_tier

# Check RAM in GB
ram_gb = psutil.virtual_memory().total / (1024**3)

Expand All @@ -66,6 +118,7 @@ def get_hardware_info() -> dict:
"ram_gb": round(psutil.virtual_memory().total / (1024**3), 2),
"gpu_detected": bool(gpu_names),
"gpu_names": gpu_names,
"apple_silicon": detect_apple_silicon(),
"available_providers": ort.get_available_providers(),
"recommended_tier": detect_hardware_tier(),
}
8 changes: 5 additions & 3 deletions frontend/src/components/OnboardingSteps/AIModelSetupStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,11 @@ export const AIModelSetupStep: React.FC<AIModelSetupStepProps> = ({
</span>
<span className="bg-muted/60 text-muted-foreground inline-flex items-center gap-1 rounded-full border px-3 py-1 text-xs font-medium">
<Cpu className="h-3 w-3" />
{hardwareSpecs.gpu_names?.length
? hardwareSpecs.gpu_names.join(', ')
: 'No dedicated GPU'}
{hardwareSpecs.apple_silicon
? hardwareSpecs.apple_silicon
: hardwareSpecs.gpu_names?.length
? hardwareSpecs.gpu_names.join(', ')
: 'No dedicated GPU'}
</span>
</div>
)}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface HardwareInfo {
ram_gb: number;
gpu_detected: boolean;
gpu_names: string[];
apple_silicon: string | null;
available_providers: string[];
recommended_tier: ModelTier;
}
Expand Down
Loading