jay-code
Version:
Streamlined AI CLI orchestration engine with mathematical rigor and enterprise-grade reliability
196 lines (172 loc) • 6.43 kB
text/typescript
/**
* System Requirements Detection for Jay-Code
* Detects VRAM, RAM, and determines optimal model configuration
*/
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
export interface SystemCapabilities {
totalVRAM: number; // MB
availableVRAM: number; // MB
totalRAM: number; // MB
availableRAM: number; // MB
gpuCount: number;
gpuModels: string[];
canRunLocal: boolean;
recommendedModel: 'local' | 'api';
platform: 'linux' | 'darwin' | 'win32';
}
export class SystemDetector {
async detectCapabilities(): Promise<SystemCapabilities> {
const platform = process.platform as 'linux' | 'darwin' | 'win32';
const [vramInfo, ramInfo, gpuInfo] = await Promise.all([
this.detectVRAM(platform),
this.detectRAM(platform),
this.detectGPUs(platform)
]);
const totalVRAM = vramInfo.total;
const availableVRAM = vramInfo.available;
const canRunLocal = totalVRAM >= 40000; // 40GB minimum
return {
totalVRAM,
availableVRAM,
totalRAM: ramInfo.total,
availableRAM: ramInfo.available,
gpuCount: gpuInfo.count,
gpuModels: gpuInfo.models,
canRunLocal,
recommendedModel: canRunLocal ? 'local' : 'api',
platform
};
}
private async detectVRAM(platform: string): Promise<{ total: number; available: number }> {
try {
if (platform === 'linux') {
// Try nvidia-smi first
try {
const { stdout } = await execAsync('nvidia-smi --query-gpu=memory.total,memory.free --format=csv,noheader,nounits');
const lines = stdout.trim().split('\n');
let totalVRAM = 0;
let availableVRAM = 0;
for (const line of lines) {
const [total, free] = line.split(', ').map(Number);
totalVRAM += total;
availableVRAM += free;
}
return { total: totalVRAM, available: availableVRAM };
} catch (nvidiaError) {
// Try ROCm for AMD GPUs
try {
const { stdout } = await execAsync('rocm-smi --showmeminfo vram');
// Parse ROCm output - this is simplified
const vramMatch = stdout.match(/(\d+)\s*MB/);
if (vramMatch) {
const total = parseInt(vramMatch[1]);
return { total, available: Math.floor(total * 0.8) };
}
} catch (rocmError) {
// Fallback to system detection
return this.detectVRAMFallback();
}
}
} else if (platform === 'darwin') {
// macOS - check for Apple Silicon unified memory
try {
const { stdout } = await execAsync('system_profiler SPHardwareDataType | grep "Memory:"');
const memMatch = stdout.match(/(\d+)\s*GB/);
if (memMatch) {
const totalGB = parseInt(memMatch[1]);
// On Apple Silicon, treat as shared VRAM (conservative estimate)
const vramMB = Math.floor(totalGB * 1024 * 0.6); // 60% of RAM as potential VRAM
return { total: vramMB, available: Math.floor(vramMB * 0.8) };
}
} catch (macError) {
return this.detectVRAMFallback();
}
}
return this.detectVRAMFallback();
} catch (error) {
console.warn('VRAM detection failed, using fallback');
return this.detectVRAMFallback();
}
}
private async detectVRAMFallback(): Promise<{ total: number; available: number }> {
// Conservative fallback - assume no dedicated VRAM
return { total: 0, available: 0 };
}
private async detectRAM(platform: string): Promise<{ total: number; available: number }> {
try {
const totalBytes = require('os').totalmem();
const freeBytes = require('os').freemem();
return {
total: Math.floor(totalBytes / 1024 / 1024), // Convert to MB
available: Math.floor(freeBytes / 1024 / 1024)
};
} catch (error) {
return { total: 8192, available: 4096 }; // 8GB fallback
}
}
private async detectGPUs(platform: string): Promise<{ count: number; models: string[] }> {
try {
if (platform === 'linux') {
const { stdout } = await execAsync('lspci | grep -i "vga\\|3d\\|display"');
const lines = stdout.trim().split('\n').filter(line => line.length > 0);
const models = lines.map(line => {
const match = line.match(/: (.+)$/);
return match ? match[1] : 'Unknown GPU';
});
return { count: models.length, models };
} else if (platform === 'darwin') {
const { stdout } = await execAsync('system_profiler SPDisplaysDataType | grep "Chipset Model:"');
const models = stdout.split('\n')
.filter(line => line.includes('Chipset Model:'))
.map(line => line.split(':')[1]?.trim() || 'Unknown GPU');
return { count: models.length, models };
}
return { count: 0, models: [] };
} catch (error) {
return { count: 0, models: [] };
}
}
async checkOllamaCompatibility(): Promise<boolean> {
const capabilities = await this.detectCapabilities();
if (!capabilities.canRunLocal) {
console.log(`Insufficient VRAM: ${capabilities.totalVRAM}MB (requires 40GB)`);
return false;
}
if (capabilities.totalRAM < 64000) {
console.log(`Insufficient RAM: ${capabilities.totalRAM}MB (requires 64GB)`);
return false;
}
return true;
}
async generateModelConfig(): Promise<any> {
const capabilities = await this.detectCapabilities();
if (capabilities.canRunLocal) {
return {
type: 'local',
primary: {
type: 'ollama',
model: 'llama3.3:70b-instruct',
endpoint: 'http://localhost:11434'
},
fallback: {
type: 'anthropic',
model: 'claude-3-5-sonnet-20241022',
apiKey: '${ANTHROPIC_API_KEY}'
},
recommendation: 'Using local Llama 3.3 70B for optimal performance'
};
} else {
return {
type: 'api',
primary: {
type: 'anthropic',
model: 'claude-3-5-sonnet-20241022',
apiKey: '${ANTHROPIC_API_KEY}'
},
recommendation: `Insufficient VRAM (${capabilities.totalVRAM}MB < 40GB), using Claude API`
};
}
}
}