import type { CompatibilityCheck, CompatibilityReport, CompatibilitySeverity, RuntimeGpuMonitoringTool, RuntimeRocmSmiTool, SystemRuntimeInfo, } from "../../models/types"; import { Effect } from "effect"; import { runCommandAsyncEffect } from "../../../core/command"; import { resolveAmdSmiBinary, resolveNvidiaSmiBinary, resolveRocmSmiBinary } from "./smi-tools"; const toEvidence = (lines: Array): string | null => { const filtered = lines.filter((line): line is string => Boolean(line && line.trim())); return filtered.length ? filtered.join("\n") : null; }; const addCheck = ( checks: CompatibilityCheck[], check: Omit & { severity: CompatibilitySeverity }, ): void => { checks.push({ id: check.id, severity: check.severity, message: check.message, evidence: check.evidence ?? null, suggested_fix: check.suggested_fix ?? null, }); }; export const probeGpuMonitoring = ( kind: SystemRuntimeInfo["platform"]["kind"], rocmTool: RuntimeRocmSmiTool | null, ): Effect.Effect<{ available: boolean; tool: RuntimeGpuMonitoringTool | null }> => { const probe = (binary: string, args: string[]): Effect.Effect => runCommandAsyncEffect(binary, args, { timeoutMs: 2_000 }).pipe( Effect.map((result) => result.status === 0), ); if (kind === "cuda") { const binary = resolveNvidiaSmiBinary(); if (!binary) return Effect.succeed({ available: false, tool: "nvidia-smi" }); return probe(binary, ["--query-gpu=name", "--format=csv,noheader,nounits"]).pipe( Effect.map((available) => ({ available, tool: "nvidia-smi" as const })), ); } if (kind === "rocm") { const preferred = rocmTool ?? (resolveAmdSmiBinary() ? "amd-smi" : null); if (preferred === "amd-smi") { const binary = resolveAmdSmiBinary(); if (!binary) return Effect.succeed({ available: false, tool: "amd-smi" }); return probe(binary, ["version"]).pipe( Effect.map((available) => ({ available, tool: "amd-smi" as const })), ); } if (preferred === "rocm-smi") { const binary = resolveRocmSmiBinary(); if (!binary) return Effect.succeed({ available: false, tool: "rocm-smi" }); return probe(binary, ["--showproductname"]).pipe( Effect.map((available) => ({ available, tool: "rocm-smi" as const })), ); } const amd = resolveAmdSmiBinary(); const rocm = resolveRocmSmiBinary(); return Effect.gen(function* () { if (amd && (yield* probe(amd, ["version"]))) { return { available: true, tool: "amd-smi" as const }; } if (rocm && (yield* probe(rocm, ["--showproductname"]))) { return { available: true, tool: "rocm-smi" as const }; } return { available: false, tool: null }; }); } return Effect.succeed({ available: false, tool: null }); }; export const buildCompatibilityReport = (args: { runtime: SystemRuntimeInfo; inference_port: number; inference_port_open: boolean; inference_process_known: boolean; gpu_monitoring: { available: boolean; tool: RuntimeGpuMonitoringTool | null }; }): CompatibilityReport => { const { runtime } = args; const checks: CompatibilityCheck[] = []; const gpuMonitoring = args.gpu_monitoring; if (runtime.gpus.count === 0) { addCheck(checks, { id: "gpu.none-detected", severity: "warn", message: "No GPUs detected by the controller.", evidence: toEvidence([ `platform.kind=${runtime.platform.kind}`, `gpus.count=${runtime.gpus.count}`, ]), suggested_fix: runtime.platform.kind === "rocm" ? "Verify ROCm is installed and GPU tools are available (amd-smi/rocm-smi)." : runtime.platform.kind === "cuda" ? "Verify NVIDIA drivers are installed and nvidia-smi is accessible." : "Verify GPU drivers are installed and set LOCAL_STUDIO_GPU_SMI_TOOL if needed.", }); } if (runtime.platform.kind === "rocm" && !runtime.platform.torch.torch_hip) { addCheck(checks, { id: "torch.rocm-missing-hip", severity: "error", message: "ROCm platform detected, but PyTorch does not report HIP support (torch.version.hip is null).", evidence: toEvidence([ `torch_version=${runtime.platform.torch.torch_version ?? "null"}`, `torch_hip=${runtime.platform.torch.torch_hip ?? "null"}`, ]), suggested_fix: "Install a ROCm-enabled PyTorch build that matches your ROCm version, and ensure the controller is using that Python environment.", }); } if (runtime.platform.kind === "rocm" && !gpuMonitoring.available) { addCheck(checks, { id: "gpu-monitoring.rocm-unavailable", severity: "warn", message: "ROCm platform detected, but GPU monitoring tooling is not accessible.", evidence: toEvidence([`tool=${gpuMonitoring.tool ?? "null"}`]), suggested_fix: "Ensure `amd-smi` or `rocm-smi` is installed and on PATH, or set AMD_SMI_PATH/ROCM_SMI_PATH.", }); } if (runtime.platform.kind === "cuda" && !gpuMonitoring.available) { addCheck(checks, { id: "gpu-monitoring.cuda-unavailable", severity: "warn", message: "CUDA platform detected, but nvidia-smi is not accessible (GPU telemetry may be unavailable).", evidence: toEvidence([`tool=${gpuMonitoring.tool ?? "nvidia-smi"}`]), suggested_fix: "Ensure NVIDIA drivers are installed and nvidia-smi is on PATH (snap-installed bun can block access).", }); } if (args.inference_port_open && !args.inference_process_known) { addCheck(checks, { id: "inference.port-in-use", severity: "error", message: "Inference port is in use by an unknown process.", evidence: toEvidence([`inference_port=${args.inference_port}`]), suggested_fix: "Stop the process using the inference port, or change LOCAL_STUDIO_INFERENCE_PORT to a free port.", }); } const mlxInstalled = runtime.backends.mlx?.installed ?? false; if ( !runtime.backends.vllm.installed && !runtime.backends.sglang.installed && !runtime.backends.llamacpp.installed && !mlxInstalled ) { addCheck(checks, { id: "backends.none-installed", severity: "info", message: "No inference runtime backends appear to be installed.", evidence: null, suggested_fix: "Install at least one backend runtime (vLLM, SGLang, llama.cpp, or MLX), then restart the controller.", }); } return { platform: { kind: runtime.platform.kind }, gpu_monitoring: gpuMonitoring, torch: runtime.platform.torch, backends: runtime.backends, checks, }; };