From d221dccbdbfdc1a86cf500fa36c21783e002abf0 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 23 Feb 2026 22:50:46 -0500 Subject: [PATCH 1/2] Probe for CUDA before attempting GPU embeddings ONNX Runtime crashes with an uncatchable native error when CUDA libraries are missing. This adds a lightweight probe (nvidia-smi + libcublasLt.so.12 path check) before selecting the CUDA device, falling back to CPU gracefully on systems without NVIDIA GPUs. Fixes #53 Co-Authored-By: Claude --- gitnexus/src/core/embeddings/embedder.ts | 28 +++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/gitnexus/src/core/embeddings/embedder.ts b/gitnexus/src/core/embeddings/embedder.ts index 32948863c0..273678ae3d 100644 --- a/gitnexus/src/core/embeddings/embedder.ts +++ b/gitnexus/src/core/embeddings/embedder.ts @@ -15,8 +15,32 @@ if (!process.env.ORT_LOG_LEVEL) { } import { pipeline, env, type FeatureExtractionPipeline } from '@huggingface/transformers'; +import { existsSync } from 'fs'; +import { execSync } from 'child_process'; import { DEFAULT_EMBEDDING_CONFIG, type EmbeddingConfig, type ModelProgress } from './types.js'; +/** + * Check whether CUDA libraries are actually available on this system. + * ONNX Runtime's native layer crashes (uncatchable) if we attempt CUDA + * without the required shared libraries, so we probe first. + */ +function isCudaAvailable(): boolean { + // Quick check: nvidia-smi exists and runs + try { + execSync('nvidia-smi', { stdio: 'ignore', timeout: 3000 }); + return true; + } catch { + // No driver or no nvidia-smi + } + // Fallback: check for the specific library ONNX needs + const libPaths = [ + '/usr/lib/x86_64-linux-gnu/libcublasLt.so.12', + '/usr/local/cuda/lib64/libcublasLt.so.12', + '/usr/lib64/libcublasLt.so.12', + ]; + return libPaths.some(p => existsSync(p)); +} + // Module-level state for singleton pattern let embedderInstance: FeatureExtractionPipeline | null = null; let isInitializing = false; @@ -62,8 +86,10 @@ export const initEmbedder = async ( const finalConfig = { ...DEFAULT_EMBEDDING_CONFIG, ...config }; // On Windows, use DirectML for GPU acceleration (via DirectX12) // CUDA is only available on Linux x64 with onnxruntime-node + // Probe for CUDA first — ONNX Runtime crashes (uncatchable native error) + // if we attempt CUDA without the required shared libraries const isWindows = process.platform === 'win32'; - const gpuDevice = isWindows ? 'dml' : 'cuda'; + const gpuDevice = isWindows ? 'dml' : (isCudaAvailable() ? 'cuda' : 'cpu'); let requestedDevice = forceDevice || (finalConfig.device === 'auto' ? gpuDevice : finalConfig.device); initPromise = (async () => { From f79cc5536b78275c0413729710bac793cfe41b34 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 24 Feb 2026 10:16:39 -0500 Subject: [PATCH 2/2] Rework CUDA probe: use ldconfig + env vars instead of nvidia-smi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses review feedback: - Remove nvidia-smi early return (driver ≠ runtime libs) - Use ldconfig -p as primary check (covers all architectures and paths) - Fall back to CUDA_PATH / LD_LIBRARY_PATH for conda, /opt/cuda, etc. - Switch from execSync to execFileSync (avoids spawning a shell) Co-Authored-By: Claude --- gitnexus/src/core/embeddings/embedder.ts | 36 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/gitnexus/src/core/embeddings/embedder.ts b/gitnexus/src/core/embeddings/embedder.ts index 273678ae3d..5829c75443 100644 --- a/gitnexus/src/core/embeddings/embedder.ts +++ b/gitnexus/src/core/embeddings/embedder.ts @@ -16,29 +16,41 @@ if (!process.env.ORT_LOG_LEVEL) { import { pipeline, env, type FeatureExtractionPipeline } from '@huggingface/transformers'; import { existsSync } from 'fs'; -import { execSync } from 'child_process'; +import { execFileSync } from 'child_process'; +import { join } from 'path'; import { DEFAULT_EMBEDDING_CONFIG, type EmbeddingConfig, type ModelProgress } from './types.js'; /** * Check whether CUDA libraries are actually available on this system. * ONNX Runtime's native layer crashes (uncatchable) if we attempt CUDA * without the required shared libraries, so we probe first. + * + * Checks the dynamic linker cache (ldconfig) which covers all architectures + * and install paths, then falls back to CUDA_PATH / LD_LIBRARY_PATH env vars. */ function isCudaAvailable(): boolean { - // Quick check: nvidia-smi exists and runs + // Primary: query the dynamic linker cache — covers all architectures, + // distro layouts, and custom install paths registered with ldconfig try { - execSync('nvidia-smi', { stdio: 'ignore', timeout: 3000 }); - return true; + const out = execFileSync('ldconfig', ['-p'], { timeout: 3000, encoding: 'utf-8' }); + if (out.includes('libcublasLt.so.12')) return true; } catch { - // No driver or no nvidia-smi + // ldconfig not available (e.g. non-standard container) } - // Fallback: check for the specific library ONNX needs - const libPaths = [ - '/usr/lib/x86_64-linux-gnu/libcublasLt.so.12', - '/usr/local/cuda/lib64/libcublasLt.so.12', - '/usr/lib64/libcublasLt.so.12', - ]; - return libPaths.some(p => existsSync(p)); + + // Fallback: check CUDA_PATH and LD_LIBRARY_PATH for environments where + // ldconfig doesn't know about the CUDA install (conda, manual /opt/cuda, etc.) + for (const envVar of ['CUDA_PATH', 'LD_LIBRARY_PATH']) { + const val = process.env[envVar]; + if (!val) continue; + for (const dir of val.split(':').filter(Boolean)) { + if (existsSync(join(dir, 'lib64', 'libcublasLt.so.12')) || + existsSync(join(dir, 'lib', 'libcublasLt.so.12')) || + existsSync(join(dir, 'libcublasLt.so.12'))) return true; + } + } + + return false; } // Module-level state for singleton pattern