diff --git a/backend/app/utils/hardware_detect.py b/backend/app/utils/hardware_detect.py index dbbd8a9a0..1a6a257a4 100644 --- a/backend/app/utils/hardware_detect.py +++ b/backend/app/utils/hardware_detect.py @@ -1,5 +1,6 @@ import shutil import subprocess +import platform import onnxruntime as ort import psutil @@ -35,11 +36,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" + + 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) @@ -66,6 +116,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(), } diff --git a/frontend/src/components/OnboardingSteps/AIModelSetupStep.tsx b/frontend/src/components/OnboardingSteps/AIModelSetupStep.tsx index 7fa264264..f41fc33d0 100644 --- a/frontend/src/components/OnboardingSteps/AIModelSetupStep.tsx +++ b/frontend/src/components/OnboardingSteps/AIModelSetupStep.tsx @@ -262,9 +262,11 @@ export const AIModelSetupStep: React.FC = ({ - {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'} )} diff --git a/frontend/src/types/models.ts b/frontend/src/types/models.ts index c8da5b4a2..5c8997bac 100644 --- a/frontend/src/types/models.ts +++ b/frontend/src/types/models.ts @@ -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; }