diff --git a/src/engine/mlx.rs b/src/engine/mlx.rs index 6d1e090..257fe1d 100644 --- a/src/engine/mlx.rs +++ b/src/engine/mlx.rs @@ -27,14 +27,7 @@ impl MLXEngine { #[cfg(target_os = "macos")] { // Check if we're on Apple Silicon (ARM64) - if std::env::consts::ARCH == "aarch64" { - // Try to detect MLX installation - // This is a simplified check - in a real implementation, - // you'd check for MLX Python packages or native libraries - Self::check_mlx_python_available() - } else { - false - } + std::env::consts::ARCH == "aarch64" } #[cfg(not(target_os = "macos"))] { @@ -42,13 +35,18 @@ impl MLXEngine { } } + /// Check if MLX hardware is supported (Apple Silicon + macOS) + pub fn is_hardware_supported() -> bool { + Self::check_mlx_availability() + } + /// Public method to check if MLX is available pub fn is_available(&self) -> bool { self.mlx_available } /// Check if MLX Python packages are available - fn check_mlx_python_available() -> bool { + pub fn check_mlx_python_available() -> bool { // Try to run a simple MLX command to verify installation Command::new("python3") .args(["-c", "import mlx.core; print('MLX available')"]) diff --git a/src/main.rs b/src/main.rs index 3957bbe..c0395f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -474,11 +474,19 @@ async fn main() -> anyhow::Result<()> { #[cfg(feature = "mlx")] { use crate::engine::mlx::MLXEngine; - let mlx_engine = MLXEngine::new(); - if mlx_engine.is_available() { - println!("🍎 MLX Backend: Available (Apple Silicon)"); + + if MLXEngine::is_hardware_supported() { + // Check if MLX Python packages are available + let python_available = MLXEngine::check_mlx_python_available(); + if python_available { + println!("🍎 MLX Backend: ✅ Available (Apple Silicon + MLX installed)"); + } else { + println!("🍎 MLX Backend: ⚠️ Hardware supported (Apple Silicon detected)"); + println!(" 📦 MLX Python packages not found"); + println!(" 💡 Install with: pip install mlx-lm"); + } } else { - println!("🍎 MLX Backend: Not available (requires Apple Silicon)"); + println!("🍎 MLX Backend: ❌ Not supported (requires Apple Silicon macOS)"); } } @@ -489,10 +497,26 @@ async fn main() -> anyhow::Result<()> { println!(); println!("💡 To enable GPU acceleration:"); - println!(" cargo install shimmy --features llama-cuda # NVIDIA CUDA"); - println!(" cargo install shimmy --features llama-vulkan # Cross-platform Vulkan"); - println!(" cargo install shimmy --features llama-opencl # AMD/Intel OpenCL"); - println!(" cargo install shimmy --features gpu # All GPU backends"); + + #[cfg(target_os = "macos")] + if std::env::consts::ARCH == "aarch64" { + println!(" cargo install shimmy --features apple # Apple Silicon optimized"); + println!(" cargo install shimmy --features gpu # All GPU backends"); + println!(" pip install mlx-lm # For MLX Python support"); + } else { + println!(" cargo install shimmy --features llama-cuda # NVIDIA CUDA"); + println!(" cargo install shimmy --features llama-vulkan # Cross-platform Vulkan"); + println!(" cargo install shimmy --features llama-opencl # AMD/Intel OpenCL"); + println!(" cargo install shimmy --features gpu # All GPU backends"); + } + + #[cfg(not(target_os = "macos"))] + { + println!(" cargo install shimmy --features llama-cuda # NVIDIA CUDA"); + println!(" cargo install shimmy --features llama-vulkan # Cross-platform Vulkan"); + println!(" cargo install shimmy --features llama-opencl # AMD/Intel OpenCL"); + println!(" cargo install shimmy --features gpu # All GPU backends"); + } } cli::Command::Init { template, diff --git a/tests/apple_silicon_detection_test.rs b/tests/apple_silicon_detection_test.rs new file mode 100644 index 0000000..cd9d282 --- /dev/null +++ b/tests/apple_silicon_detection_test.rs @@ -0,0 +1,105 @@ +/// Apple Silicon GPU Detection Regression Test +/// +/// Tests for issue #87: "Cannot get apple gpu info" +/// Ensures Apple Silicon hardware detection works properly regardless of MLX Python package installation +/// +/// Issue: User with Apple M3 Pro received "MLX Backend: Not available (requires Apple Silicon)" +/// Root cause: check_mlx_availability() was checking for MLX Python packages instead of just hardware +/// Solution: Separate hardware detection from software installation checks + +#[cfg(test)] +mod apple_silicon_tests { + use shimmy::engine::mlx::MLXEngine; + + #[test] + fn test_hardware_detection_independent_of_python_packages() { + // This test ensures that Apple Silicon hardware detection + // works regardless of whether MLX Python packages are installed + + let hardware_supported = MLXEngine::is_hardware_supported(); + let python_available = MLXEngine::check_mlx_python_available(); + + // On Apple Silicon macOS, hardware should be detected even if Python packages aren't installed + #[cfg(all(target_os = "macos", target_arch = "aarch64"))] + { + // Hardware should always be detected on Apple Silicon macOS + assert!(hardware_supported, + "Apple Silicon hardware should be detected on aarch64 macOS"); + + // Python packages may or may not be installed, that's separate + // We don't assert anything about python_available since it depends on user setup + println!("Apple Silicon detected: {}", hardware_supported); + println!("MLX Python packages available: {}", python_available); + } + + // On non-Apple Silicon systems, hardware should not be detected + #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))] + { + assert!(!hardware_supported, + "Apple Silicon hardware should not be detected on non-Apple Silicon systems"); + + // Python packages might still be installed on non-Apple Silicon (e.g., for development) + // but that's not what we're testing here + println!("Apple Silicon detected: {}", hardware_supported); + println!("MLX Python packages available: {}", python_available); + } + } + + #[test] + fn test_mlx_engine_creation() { + // Test that MLXEngine can be created without panicking + let engine = MLXEngine::new(); + + // The engine should reflect hardware capabilities + let is_available = engine.is_available(); + let hardware_supported = MLXEngine::is_hardware_supported(); + + // These should be consistent + assert_eq!(is_available, hardware_supported, + "Engine availability should match hardware support"); + } + + #[test] + fn test_gpu_info_output_regression() { + // This test ensures that gpu-info command would provide helpful output + // for Apple Silicon users, even without MLX Python packages + + #[cfg(all(target_os = "macos", target_arch = "aarch64"))] + { + let hardware_supported = MLXEngine::is_hardware_supported(); + let python_available = MLXEngine::check_mlx_python_available(); + + // Simulate the output logic from main.rs + if hardware_supported { + if python_available { + println!("🍎 MLX Backend: ✅ Available (Apple Silicon + MLX installed)"); + } else { + println!("🍎 MLX Backend: ⚠️ Hardware supported (Apple Silicon detected)"); + println!(" 📦 MLX Python packages not found"); + println!(" 💡 Install with: pip install mlx-lm"); + } + } else { + println!("🍎 MLX Backend: ❌ Not supported (requires Apple Silicon macOS)"); + } + + // The key fix: we should never see "Not supported" on Apple Silicon + assert!(hardware_supported, + "Apple Silicon users should see 'Hardware supported' not 'Not supported'"); + } + } + + #[test] + fn test_python_detection_graceful_failure() { + // Ensure MLX Python detection fails gracefully when python3 is not available + // or when MLX packages are not installed + + let python_available = MLXEngine::check_mlx_python_available(); + + // This should not panic regardless of system state + // Result can be true or false depending on whether MLX is installed + println!("MLX Python packages detected: {}", python_available); + + // Test should pass whether MLX is installed or not + assert!(true, "Python detection should complete without panic"); + } +} \ No newline at end of file