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
16 changes: 7 additions & 9 deletions src/engine/mlx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,26 @@ 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"))]
{
false
}
}

/// 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')"])
Expand Down
40 changes: 32 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)");
}
}

Expand All @@ -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,
Expand Down
105 changes: 105 additions & 0 deletions tests/apple_silicon_detection_test.rs
Original file line number Diff line number Diff line change
@@ -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");
}
}
Loading