From 2eee17e159152f00ef161c08831c1d03e365a9fd Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Tue, 6 Jan 2026 09:53:52 -0600 Subject: [PATCH] fix(vision): handle unsupported platforms gracefully for training MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add platform detection before attempting TensorFlow installation. TensorFlow doesn't have wheels for ARM Linux (like Synology NAS), so the hardware detection now returns a clear "Platform Not Supported" message instead of failing during pip install. Changes: - Add isPlatformSupported() check in config.ts - Check platform before attempting venv setup in hardware/route.ts - Check platform before starting training in train/route.ts - Show user-friendly message in HardwareCard for unsupported platforms - Add data/vision-training/collected/ to .gitignore 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- apps/web/.gitignore | 1 + .../web/src/app/api/vision-training/config.ts | 39 +++++++++++++++ .../app/api/vision-training/hardware/route.ts | 22 +++++++- .../app/api/vision-training/train/route.ts | 15 +++++- .../components/wizard/cards/HardwareCard.tsx | 50 +++++++++++-------- 5 files changed, 105 insertions(+), 22 deletions(-) diff --git a/apps/web/.gitignore b/apps/web/.gitignore index 1a49a7f0..7ee19892 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -63,4 +63,5 @@ data/uploads/ # ML training data training-data/ +data/vision-training/collected/ scripts/train-column-classifier/.venv/ diff --git a/apps/web/src/app/api/vision-training/config.ts b/apps/web/src/app/api/vision-training/config.ts index 5b5542f9..b1d5f4e5 100644 --- a/apps/web/src/app/api/vision-training/config.ts +++ b/apps/web/src/app/api/vision-training/config.ts @@ -15,6 +15,45 @@ const execAsync = promisify(exec) const cwd = process.cwd() +/** + * Check if the current platform supports TensorFlow training. + * TensorFlow doesn't have wheels for all platforms (e.g., ARM-based NAS devices). + */ +export function isPlatformSupported(): { supported: boolean; reason?: string } { + const platform = process.platform + const arch = process.arch + + // TensorFlow supports: + // - macOS on x86_64 and arm64 (Apple Silicon with tensorflow-macos) + // - Linux on x86_64 (and some arm64 builds) + // - Windows on x86_64 + + if (platform === 'darwin') { + // macOS - both Intel and Apple Silicon are supported + return { supported: true } + } + + if (platform === 'linux') { + if (arch === 'x64') { + return { supported: true } + } + // ARM Linux (like Synology NAS) typically doesn't have TensorFlow wheels + return { + supported: false, + reason: `TensorFlow is not available for Linux ${arch}. Training should be done on a machine with x86_64 or Apple Silicon.`, + } + } + + if (platform === 'win32' && arch === 'x64') { + return { supported: true } + } + + return { + supported: false, + reason: `TensorFlow is not available for ${platform} ${arch}. Training should be done on macOS, Linux x86_64, or Windows x86_64.`, + } +} + /** * Path to the training scripts directory */ diff --git a/apps/web/src/app/api/vision-training/hardware/route.ts b/apps/web/src/app/api/vision-training/hardware/route.ts index f956a66f..7b0e065c 100644 --- a/apps/web/src/app/api/vision-training/hardware/route.ts +++ b/apps/web/src/app/api/vision-training/hardware/route.ts @@ -1,6 +1,6 @@ import { spawn } from 'child_process' import path from 'path' -import { ensureVenvReady, PYTHON_ENV, TRAINING_PYTHON, TRAINING_SCRIPTS_DIR } from '../config' +import { ensureVenvReady, isPlatformSupported, PYTHON_ENV, TRAINING_PYTHON, TRAINING_SCRIPTS_DIR } from '../config' /** * Hardware detection result from Python/TensorFlow @@ -28,6 +28,26 @@ const CACHE_TTL_MS = 60 * 60 * 1000 // 1 hour * Runs a Python script that queries TensorFlow for available devices. */ export async function GET(): Promise { + // Check platform support first - don't even try to set up venv on unsupported platforms + const platformCheck = isPlatformSupported() + if (!platformCheck.supported) { + const unsupportedResult: HardwareInfo = { + available: false, + device: 'unsupported', + deviceName: 'Platform Not Supported', + deviceType: 'unsupported', + details: { + platform: process.platform, + arch: process.arch, + }, + error: platformCheck.reason || 'This platform does not support TensorFlow training', + } + return new Response(JSON.stringify(unsupportedResult), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }) + } + // Return cached result if valid const now = Date.now() if (cachedHardwareInfo && now - cacheTimestamp < CACHE_TTL_MS) { diff --git a/apps/web/src/app/api/vision-training/train/route.ts b/apps/web/src/app/api/vision-training/train/route.ts index bb61fb82..308525ff 100644 --- a/apps/web/src/app/api/vision-training/train/route.ts +++ b/apps/web/src/app/api/vision-training/train/route.ts @@ -1,6 +1,6 @@ import { spawn, type ChildProcess } from 'child_process' import path from 'path' -import { ensureVenvReady, PYTHON_ENV, TRAINING_PYTHON } from '../config' +import { ensureVenvReady, isPlatformSupported, PYTHON_ENV, TRAINING_PYTHON } from '../config' /** * Training configuration options @@ -25,6 +25,19 @@ let activeAbortController: AbortController | null = null * Only one training session can run at a time. */ export async function POST(request: Request): Promise { + // Check platform support first + const platformCheck = isPlatformSupported() + if (!platformCheck.supported) { + return new Response( + JSON.stringify({ + error: 'Platform not supported', + details: platformCheck.reason, + hint: 'Training should be done on macOS, Linux x86_64, or Windows x86_64', + }), + { status: 400, headers: { 'Content-Type': 'application/json' } } + ) + } + // Check if training is already running if (activeProcess && !activeProcess.killed) { return new Response( diff --git a/apps/web/src/app/vision-training/train/components/wizard/cards/HardwareCard.tsx b/apps/web/src/app/vision-training/train/components/wizard/cards/HardwareCard.tsx index 715d968b..3e1f193d 100644 --- a/apps/web/src/app/vision-training/train/components/wizard/cards/HardwareCard.tsx +++ b/apps/web/src/app/vision-training/train/components/wizard/cards/HardwareCard.tsx @@ -70,29 +70,39 @@ export function HardwareCard({ } if (hardwareInfo?.error) { + const isUnsupportedPlatform = hardwareInfo.device === 'unsupported' + return (
-
⚠️
-
Hardware setup failed
-
- {hardwareInfo.error} +
+ {isUnsupportedPlatform ? '🚫' : '⚠️'}
- +
+ {isUnsupportedPlatform ? 'Platform Not Supported' : 'Hardware setup failed'} +
+
+ {isUnsupportedPlatform + ? 'Training requires macOS, Linux x86_64, or Windows. Run training on your local development machine instead.' + : hardwareInfo.error} +
+ {!isUnsupportedPlatform && ( + + )}
) }