mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 09:33:10 -07:00
warnings for users that don't have cuda/rocm installed but use binaries that support them
This commit is contained in:
parent
dd3b1c2593
commit
f681129aae
15 changed files with 272 additions and 68 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
alsa
|
||||||
AMDGPU
|
AMDGPU
|
||||||
APPIMAGE
|
APPIMAGE
|
||||||
asar
|
asar
|
||||||
|
|
@ -29,6 +30,7 @@ KOBOLDAI
|
||||||
koboldcpp
|
koboldcpp
|
||||||
KoboldCpp
|
KoboldCpp
|
||||||
KOBOLDCPP
|
KOBOLDCPP
|
||||||
|
libxss
|
||||||
lora
|
lora
|
||||||
lowvram
|
lowvram
|
||||||
Lowvram
|
Lowvram
|
||||||
|
|
@ -50,8 +52,16 @@ nvidia
|
||||||
oldpc
|
oldpc
|
||||||
OLDPC
|
OLDPC
|
||||||
opencl
|
opencl
|
||||||
|
optdepends
|
||||||
paru
|
paru
|
||||||
pkexec
|
pkexec
|
||||||
|
pkgbase
|
||||||
|
PKGBUILD
|
||||||
|
pkgdesc
|
||||||
|
pkgdir
|
||||||
|
pkgname
|
||||||
|
pkgrel
|
||||||
|
pkgver
|
||||||
quantmatmul
|
quantmatmul
|
||||||
radeon
|
radeon
|
||||||
remotetunnel
|
remotetunnel
|
||||||
|
|
@ -70,6 +80,8 @@ SDXL
|
||||||
Segoe
|
Segoe
|
||||||
sonarjs
|
sonarjs
|
||||||
SPACEBAR
|
SPACEBAR
|
||||||
|
squashfs
|
||||||
|
SRCINFO
|
||||||
taskkill
|
taskkill
|
||||||
Tauri
|
Tauri
|
||||||
togglefullscreen
|
togglefullscreen
|
||||||
|
|
|
||||||
4
.github/workflows/aur-release.yml
vendored
4
.github/workflows/aur-release.yml
vendored
|
|
@ -100,7 +100,7 @@ jobs:
|
||||||
# Maintainer: ${{ steps.release_info.outputs.author_name }} <${{ steps.release_info.outputs.author_email }}>
|
# Maintainer: ${{ steps.release_info.outputs.author_name }} <${{ steps.release_info.outputs.author_email }}>
|
||||||
pkgname=friendly-kobold
|
pkgname=friendly-kobold
|
||||||
pkgver=${{ steps.release_info.outputs.version }}
|
pkgver=${{ steps.release_info.outputs.version }}
|
||||||
pkgrel=2
|
pkgrel=3
|
||||||
pkgdesc="A desktop app for running Large Language Models locally"
|
pkgdesc="A desktop app for running Large Language Models locally"
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url="https://github.com/lone-cloud/friendly-kobold"
|
url="https://github.com/lone-cloud/friendly-kobold"
|
||||||
|
|
@ -167,7 +167,7 @@ jobs:
|
||||||
pkgbase = friendly-kobold
|
pkgbase = friendly-kobold
|
||||||
pkgdesc = A desktop app for running Large Language Models locally
|
pkgdesc = A desktop app for running Large Language Models locally
|
||||||
pkgver = ${{ steps.release_info.outputs.version }}
|
pkgver = ${{ steps.release_info.outputs.version }}
|
||||||
pkgrel = 2
|
pkgrel = 3
|
||||||
url = https://github.com/lone-cloud/friendly-kobold
|
url = https://github.com/lone-cloud/friendly-kobold
|
||||||
arch = x86_64
|
arch = x86_64
|
||||||
license = AGPL-3.0-or-later
|
license = AGPL-3.0-or-later
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "friendly-kobold",
|
"name": "friendly-kobold",
|
||||||
"productName": "Friendly Kobold",
|
"productName": "Friendly Kobold",
|
||||||
"version": "0.6.1",
|
"version": "0.6.2",
|
||||||
"description": "A desktop app for running Large Language Models locally",
|
"description": "A desktop app for running Large Language Models locally",
|
||||||
"main": "out/main/index.js",
|
"main": "out/main/index.js",
|
||||||
"homepage": "./",
|
"homepage": "./",
|
||||||
|
|
|
||||||
|
|
@ -33,16 +33,14 @@ export const AdvancedTab = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const detectBackendSupport = async () => {
|
const detectBackendSupport = async () => {
|
||||||
try {
|
try {
|
||||||
const currentBinaryInfo =
|
const support = await window.electronAPI.kobold.detectBackendSupport();
|
||||||
await window.electronAPI.kobold.getCurrentBinaryInfo();
|
if (support) {
|
||||||
if (currentBinaryInfo?.path) {
|
|
||||||
const support = await window.electronAPI.kobold.detectBackendSupport(
|
|
||||||
currentBinaryInfo.path
|
|
||||||
);
|
|
||||||
setBackendSupport({
|
setBackendSupport({
|
||||||
noavx2: support.noavx2,
|
noavx2: support.noavx2,
|
||||||
failsafe: support.failsafe,
|
failsafe: support.failsafe,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
setBackendSupport({ noavx2: false, failsafe: false });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.electronAPI.logs.logError(
|
window.electronAPI.logs.logError(
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useState, useEffect, useRef } from 'react';
|
||||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||||
import { BackendSelectItem } from '@/components/screens/Launch/GeneralTab/BackendSelectItem';
|
import { BackendSelectItem } from '@/components/screens/Launch/GeneralTab/BackendSelectItem';
|
||||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
|
import { checkBackendWarnings } from '@/utils/backendWarnings';
|
||||||
|
|
||||||
interface BackendSelectorProps {
|
interface BackendSelectorProps {
|
||||||
onWarningsChange?: (
|
onWarningsChange?: (
|
||||||
|
|
@ -89,41 +90,26 @@ export const BackendSelector = ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!onWarningsChange) return;
|
if (!onWarningsChange) return;
|
||||||
|
|
||||||
if (backend !== 'cpu' || !cpuCapabilities) {
|
const updateWarnings = async () => {
|
||||||
onWarningsChange([]);
|
try {
|
||||||
return;
|
const warnings = await checkBackendWarnings({
|
||||||
}
|
backend,
|
||||||
|
cpuCapabilities,
|
||||||
|
noavx2,
|
||||||
|
failsafe,
|
||||||
|
availableBackends,
|
||||||
|
});
|
||||||
|
onWarningsChange(warnings);
|
||||||
|
} catch (error) {
|
||||||
|
window.electronAPI.logs.logError(
|
||||||
|
'Failed to check backend warnings:',
|
||||||
|
error as Error
|
||||||
|
);
|
||||||
|
onWarningsChange([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const warnings: Array<{ type: 'warning' | 'info'; message: string }> = [];
|
updateWarnings();
|
||||||
|
|
||||||
if (!cpuCapabilities.avx2 && !noavx2) {
|
|
||||||
warnings.push({
|
|
||||||
type: 'warning',
|
|
||||||
message:
|
|
||||||
'Your CPU does not support AVX2. Enable the "Disable AVX2" option on the Advanced tab to avoid crashes.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cpuCapabilities.avx && !cpuCapabilities.avx2 && !failsafe) {
|
|
||||||
warnings.push({
|
|
||||||
type: 'warning',
|
|
||||||
message:
|
|
||||||
'Your CPU does not support AVX or AVX2. Enable the "Failsafe" option on the Advanced tab to avoid crashes.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
availableBackends.length > 0 &&
|
|
||||||
availableBackends.some((b) => b.value === 'cpu')
|
|
||||||
) {
|
|
||||||
warnings.push({
|
|
||||||
type: 'info',
|
|
||||||
message:
|
|
||||||
"Performance Note: LLMs run significantly faster on GPU-accelerated systems. Consider using NVIDIA's CUDA or AMD's ROCm backends for optimal performance.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onWarningsChange(warnings);
|
|
||||||
}, [
|
}, [
|
||||||
backend,
|
backend,
|
||||||
cpuCapabilities,
|
cpuCapabilities,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class FriendlyKoboldApp {
|
||||||
this.ensureInstallDirectory();
|
this.ensureInstallDirectory();
|
||||||
this.windowManager = new WindowManager();
|
this.windowManager = new WindowManager();
|
||||||
this.githubService = new GitHubService(this.logManager);
|
this.githubService = new GitHubService(this.logManager);
|
||||||
this.hardwareService = new HardwareService();
|
this.hardwareService = new HardwareService(this.logManager);
|
||||||
|
|
||||||
this.koboldManager = new KoboldCppManager(
|
this.koboldManager = new KoboldCppManager(
|
||||||
this.configManager,
|
this.configManager,
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,10 @@ export class KoboldCppManager {
|
||||||
try {
|
try {
|
||||||
unlinkSync(tempPackedFilePath);
|
unlinkSync(tempPackedFilePath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
this.logManager.logError(
|
||||||
console.warn('Failed to cleanup packed file:', error);
|
'Failed to cleanup packed file:',
|
||||||
|
error as Error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const launcherPath = this.getLauncherPath(unpackedDirPath);
|
const launcherPath = this.getLauncherPath(unpackedDirPath);
|
||||||
|
|
@ -700,8 +702,10 @@ export class KoboldCppManager {
|
||||||
try {
|
try {
|
||||||
unlinkSync(tempPackedFilePath);
|
unlinkSync(tempPackedFilePath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
this.logManager.logError(
|
||||||
console.warn('Failed to cleanup packed ROCm file:', error);
|
'Failed to cleanup packed ROCm file:',
|
||||||
|
error as Error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const launcherPath = this.getLauncherPath(unpackedDirPath);
|
const launcherPath = this.getLauncherPath(unpackedDirPath);
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@ export class BinaryService {
|
||||||
this.hardwareService = hardwareService;
|
this.hardwareService = hardwareService;
|
||||||
}
|
}
|
||||||
|
|
||||||
detectBackendSupport(koboldBinaryPath: string): BackendSupport {
|
private detectBackendSupportFromPath(
|
||||||
|
koboldBinaryPath: string
|
||||||
|
): BackendSupport {
|
||||||
if (this.backendSupportCache.has(koboldBinaryPath)) {
|
if (this.backendSupportCache.has(koboldBinaryPath)) {
|
||||||
return this.backendSupportCache.get(koboldBinaryPath)!;
|
return this.backendSupportCache.get(koboldBinaryPath)!;
|
||||||
}
|
}
|
||||||
|
|
@ -87,6 +89,24 @@ export class BinaryService {
|
||||||
return support;
|
return support;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async detectBackendSupport(): Promise<BackendSupport | null> {
|
||||||
|
try {
|
||||||
|
const currentBinaryInfo = await this.koboldManager.getCurrentBinaryInfo();
|
||||||
|
|
||||||
|
if (!currentBinaryInfo?.path) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.detectBackendSupportFromPath(currentBinaryInfo.path);
|
||||||
|
} catch (error) {
|
||||||
|
this.logManager.logError(
|
||||||
|
'Error detecting current binary backend support:',
|
||||||
|
error as Error
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getAvailableBackends(): Promise<
|
async getAvailableBackends(): Promise<
|
||||||
Array<{ value: string; label: string; devices?: string[] }>
|
Array<{ value: string; label: string; devices?: string[] }>
|
||||||
> {
|
> {
|
||||||
|
|
@ -106,7 +126,12 @@ export class BinaryService {
|
||||||
return this.availableBackendsCache.get(cacheKey)!;
|
return this.availableBackendsCache.get(cacheKey)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backendSupport = this.detectBackendSupport(currentBinaryInfo.path);
|
const backendSupport = await this.detectBackendSupport();
|
||||||
|
|
||||||
|
if (!backendSupport) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const backends: Array<{
|
const backends: Array<{
|
||||||
value: string;
|
value: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,7 @@ export class GitHubService {
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
// eslint-disable-next-line no-console
|
this.logManager.logError(
|
||||||
console.warn(
|
|
||||||
'GitHub API rate limit reached, using cached data if available'
|
'GitHub API rate limit reached, using cached data if available'
|
||||||
);
|
);
|
||||||
return this.cachedRelease
|
return this.cachedRelease
|
||||||
|
|
@ -78,8 +77,7 @@ export class GitHubService {
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
// eslint-disable-next-line no-console
|
this.logManager.logError(
|
||||||
console.warn(
|
|
||||||
'GitHub API rate limit reached, using cached data if available'
|
'GitHub API rate limit reached, using cached data if available'
|
||||||
);
|
);
|
||||||
return this.cachedRelease;
|
return this.cachedRelease;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
/* eslint-disable no-comments/disallowComments */
|
/* eslint-disable no-comments/disallowComments */
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
import { shortenDeviceName } from '@/utils';
|
import { shortenDeviceName } from '@/utils';
|
||||||
|
import { LogManager } from '@/main/managers/LogManager';
|
||||||
import type {
|
import type {
|
||||||
CPUCapabilities,
|
CPUCapabilities,
|
||||||
GPUCapabilities,
|
GPUCapabilities,
|
||||||
BasicGPUInfo,
|
BasicGPUInfo,
|
||||||
HardwareInfo,
|
HardwareInfo,
|
||||||
|
GPUMemoryInfo,
|
||||||
} from '@/types/hardware';
|
} from '@/types/hardware';
|
||||||
|
|
||||||
export class HardwareService {
|
export class HardwareService {
|
||||||
private cpuCapabilitiesCache: CPUCapabilities | null = null;
|
private cpuCapabilitiesCache: CPUCapabilities | null = null;
|
||||||
private basicGPUInfoCache: BasicGPUInfo | null = null;
|
private basicGPUInfoCache: BasicGPUInfo | null = null;
|
||||||
private gpuCapabilitiesCache: GPUCapabilities | null = null;
|
private gpuCapabilitiesCache: GPUCapabilities | null = null;
|
||||||
|
private gpuMemoryInfoCache: GPUMemoryInfo[] | null = null;
|
||||||
|
private logManager: LogManager;
|
||||||
|
|
||||||
|
constructor(logManager: LogManager) {
|
||||||
|
this.logManager = logManager;
|
||||||
|
}
|
||||||
|
|
||||||
async detectCPU(): Promise<CPUCapabilities> {
|
async detectCPU(): Promise<CPUCapabilities> {
|
||||||
if (this.cpuCapabilitiesCache) {
|
if (this.cpuCapabilitiesCache) {
|
||||||
|
|
@ -37,8 +45,7 @@ export class HardwareService {
|
||||||
|
|
||||||
return this.cpuCapabilitiesCache;
|
return this.cpuCapabilitiesCache;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
this.logManager.logError('CPU detection failed:', error as Error);
|
||||||
console.warn('CPU detection failed:', error);
|
|
||||||
const fallbackCapabilities = {
|
const fallbackCapabilities = {
|
||||||
avx: false,
|
avx: false,
|
||||||
avx2: false,
|
avx2: false,
|
||||||
|
|
@ -98,8 +105,7 @@ export class HardwareService {
|
||||||
|
|
||||||
return this.basicGPUInfoCache;
|
return this.basicGPUInfoCache;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
this.logManager.logError('GPU detection failed:', error as Error);
|
||||||
console.warn('GPU detection failed:', error);
|
|
||||||
const fallbackGPUInfo = {
|
const fallbackGPUInfo = {
|
||||||
hasAMD: false,
|
hasAMD: false,
|
||||||
hasNVIDIA: false,
|
hasNVIDIA: false,
|
||||||
|
|
@ -443,4 +449,37 @@ export class HardwareService {
|
||||||
return { supported: false, devices: [] };
|
return { supported: false, devices: [] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async detectGPUMemory(): Promise<GPUMemoryInfo[]> {
|
||||||
|
if (this.gpuMemoryInfoCache) {
|
||||||
|
return this.gpuMemoryInfoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoryInfo: GPUMemoryInfo[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const graphics = await si.graphics();
|
||||||
|
|
||||||
|
for (const controller of graphics.controllers) {
|
||||||
|
if (controller.model) {
|
||||||
|
let vram = controller.vram;
|
||||||
|
// systeminformation returns 0 or 1 if unknown/invalid
|
||||||
|
if (!vram || vram === 1) {
|
||||||
|
vram = null;
|
||||||
|
}
|
||||||
|
memoryInfo.push({
|
||||||
|
deviceName: shortenDeviceName(controller.model),
|
||||||
|
totalMemoryMB: vram,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.gpuMemoryInfoCache = memoryInfo;
|
||||||
|
} catch (error) {
|
||||||
|
this.logManager.logError('GPU memory detection failed:', error as Error);
|
||||||
|
this.gpuMemoryInfoCache = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.gpuMemoryInfoCache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,10 @@ export class IPCHandlers {
|
||||||
this.hardwareService.detectGPUCapabilities()
|
this.hardwareService.detectGPUCapabilities()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ipcMain.handle('kobold:detectGPUMemory', () =>
|
||||||
|
this.hardwareService.detectGPUMemory()
|
||||||
|
);
|
||||||
|
|
||||||
ipcMain.handle('kobold:detectROCm', () =>
|
ipcMain.handle('kobold:detectROCm', () =>
|
||||||
this.hardwareService.detectROCm()
|
this.hardwareService.detectROCm()
|
||||||
);
|
);
|
||||||
|
|
@ -120,8 +124,8 @@ export class IPCHandlers {
|
||||||
this.hardwareService.detectAllWithCapabilities()
|
this.hardwareService.detectAllWithCapabilities()
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.handle('kobold:detectBackendSupport', (_, binaryPath: string) =>
|
ipcMain.handle('kobold:detectBackendSupport', () =>
|
||||||
this.binaryService.detectBackendSupport(binaryPath)
|
this.binaryService.detectBackendSupport()
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.handle('kobold:getAvailableBackends', () =>
|
ipcMain.handle('kobold:getAvailableBackends', () =>
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,19 @@ const koboldAPI: KoboldAPI = {
|
||||||
getPlatform: () => ipcRenderer.invoke('kobold:getPlatform'),
|
getPlatform: () => ipcRenderer.invoke('kobold:getPlatform'),
|
||||||
detectGPU: () => ipcRenderer.invoke('kobold:detectGPU'),
|
detectGPU: () => ipcRenderer.invoke('kobold:detectGPU'),
|
||||||
detectCPU: () => ipcRenderer.invoke('kobold:detectCPU'),
|
detectCPU: () => ipcRenderer.invoke('kobold:detectCPU'),
|
||||||
|
detectGPUCapabilities: () =>
|
||||||
|
ipcRenderer.invoke('kobold:detectGPUCapabilities'),
|
||||||
|
detectGPUMemory: () => ipcRenderer.invoke('kobold:detectGPUMemory'),
|
||||||
detectROCm: () => ipcRenderer.invoke('kobold:detectROCm'),
|
detectROCm: () => ipcRenderer.invoke('kobold:detectROCm'),
|
||||||
detectBackendSupport: (binaryPath: string) =>
|
detectBackendSupport: () => ipcRenderer.invoke('kobold:detectBackendSupport'),
|
||||||
ipcRenderer.invoke('kobold:detectBackendSupport', binaryPath),
|
|
||||||
getAvailableBackends: () => ipcRenderer.invoke('kobold:getAvailableBackends'),
|
getAvailableBackends: () => ipcRenderer.invoke('kobold:getAvailableBackends'),
|
||||||
getCurrentInstallDir: () => ipcRenderer.invoke('kobold:getCurrentInstallDir'),
|
getCurrentInstallDir: () => ipcRenderer.invoke('kobold:getCurrentInstallDir'),
|
||||||
selectInstallDirectory: () =>
|
selectInstallDirectory: () =>
|
||||||
ipcRenderer.invoke('kobold:selectInstallDirectory'),
|
ipcRenderer.invoke('kobold:selectInstallDirectory'),
|
||||||
downloadRelease: (asset) =>
|
downloadRelease: (asset) =>
|
||||||
ipcRenderer.invoke('kobold:downloadRelease', asset),
|
ipcRenderer.invoke('kobold:downloadRelease', asset),
|
||||||
launchKoboldCpp: (args?: string[], configFilePath?: string) =>
|
launchKoboldCpp: (args?: string[]) =>
|
||||||
ipcRenderer.invoke('kobold:launchKoboldCpp', args, configFilePath),
|
ipcRenderer.invoke('kobold:launchKoboldCpp', args),
|
||||||
getConfigFiles: () => ipcRenderer.invoke('kobold:getConfigFiles'),
|
getConfigFiles: () => ipcRenderer.invoke('kobold:getConfigFiles'),
|
||||||
saveConfigFile: (
|
saveConfigFile: (
|
||||||
configName: string,
|
configName: string,
|
||||||
|
|
|
||||||
10
src/types/electron.d.ts
vendored
10
src/types/electron.d.ts
vendored
|
|
@ -4,6 +4,7 @@ import type {
|
||||||
BasicGPUInfo,
|
BasicGPUInfo,
|
||||||
HardwareInfo,
|
HardwareInfo,
|
||||||
PlatformInfo,
|
PlatformInfo,
|
||||||
|
GPUMemoryInfo,
|
||||||
} from '@/types/hardware';
|
} from '@/types/hardware';
|
||||||
|
|
||||||
interface GitHubAsset {
|
interface GitHubAsset {
|
||||||
|
|
@ -67,15 +68,17 @@ export interface KoboldAPI {
|
||||||
getPlatform: () => Promise<PlatformInfo>;
|
getPlatform: () => Promise<PlatformInfo>;
|
||||||
detectGPU: () => Promise<BasicGPUInfo>;
|
detectGPU: () => Promise<BasicGPUInfo>;
|
||||||
detectCPU: () => Promise<CPUCapabilities>;
|
detectCPU: () => Promise<CPUCapabilities>;
|
||||||
|
detectGPUCapabilities: () => Promise<GPUCapabilities>;
|
||||||
|
detectGPUMemory: () => Promise<GPUMemoryInfo[]>;
|
||||||
detectROCm: () => Promise<{ supported: boolean; devices: string[] }>;
|
detectROCm: () => Promise<{ supported: boolean; devices: string[] }>;
|
||||||
detectBackendSupport: (binaryPath: string) => Promise<{
|
detectBackendSupport: () => Promise<{
|
||||||
rocm: boolean;
|
rocm: boolean;
|
||||||
vulkan: boolean;
|
vulkan: boolean;
|
||||||
clblast: boolean;
|
clblast: boolean;
|
||||||
noavx2: boolean;
|
noavx2: boolean;
|
||||||
failsafe: boolean;
|
failsafe: boolean;
|
||||||
cuda: boolean;
|
cuda: boolean;
|
||||||
}>;
|
} | null>;
|
||||||
getAvailableBackends: () => Promise<
|
getAvailableBackends: () => Promise<
|
||||||
Array<{ value: string; label: string; devices?: string[] }>
|
Array<{ value: string; label: string; devices?: string[] }>
|
||||||
>;
|
>;
|
||||||
|
|
@ -92,8 +95,7 @@ export interface KoboldAPI {
|
||||||
getROCmDownload: () => Promise<DownloadItem | null>;
|
getROCmDownload: () => Promise<DownloadItem | null>;
|
||||||
getLatestReleaseWithStatus: () => Promise<ReleaseWithStatus | null>;
|
getLatestReleaseWithStatus: () => Promise<ReleaseWithStatus | null>;
|
||||||
launchKoboldCpp: (
|
launchKoboldCpp: (
|
||||||
args?: string[],
|
args?: string[]
|
||||||
configFilePath?: string
|
|
||||||
) => Promise<{ success: boolean; pid?: number; error?: string }>;
|
) => Promise<{ success: boolean; pid?: number; error?: string }>;
|
||||||
getConfigFiles: () => Promise<
|
getConfigFiles: () => Promise<
|
||||||
Array<{ name: string; path: string; size: number }>
|
Array<{ name: string; path: string; size: number }>
|
||||||
|
|
|
||||||
5
src/types/hardware.d.ts
vendored
5
src/types/hardware.d.ts
vendored
|
|
@ -4,6 +4,11 @@ export interface CPUCapabilities {
|
||||||
devices: string[];
|
devices: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GPUMemoryInfo {
|
||||||
|
deviceName: string;
|
||||||
|
totalMemoryMB: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GPUCapabilities {
|
export interface GPUCapabilities {
|
||||||
cuda: {
|
cuda: {
|
||||||
supported: boolean;
|
supported: boolean;
|
||||||
|
|
|
||||||
129
src/utils/backendWarnings.ts
Normal file
129
src/utils/backendWarnings.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
export interface BackendWarning {
|
||||||
|
type: 'warning' | 'info';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WarningParams {
|
||||||
|
backend: string;
|
||||||
|
cpuCapabilities: {
|
||||||
|
avx: boolean;
|
||||||
|
avx2: boolean;
|
||||||
|
} | null;
|
||||||
|
noavx2: boolean;
|
||||||
|
failsafe: boolean;
|
||||||
|
availableBackends: Array<{
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
devices?: string[];
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const checkBackendWarnings = async (
|
||||||
|
params?: WarningParams
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
|
): Promise<BackendWarning[]> => {
|
||||||
|
const warnings: BackendWarning[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [backendSupport, gpuCapabilities] = await Promise.all([
|
||||||
|
window.electronAPI.kobold.detectBackendSupport(),
|
||||||
|
window.electronAPI.kobold.detectGPUCapabilities(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!backendSupport) {
|
||||||
|
return warnings;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backendSupport.cuda && !gpuCapabilities.cuda.supported) {
|
||||||
|
warnings.push({
|
||||||
|
type: 'warning',
|
||||||
|
message:
|
||||||
|
'Your KoboldCpp binary supports CUDA, but CUDA runtime is not detected on your system.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backendSupport.rocm && !gpuCapabilities.rocm.supported) {
|
||||||
|
warnings.push({
|
||||||
|
type: 'warning',
|
||||||
|
message:
|
||||||
|
'Your KoboldCpp binary supports ROCm, but ROCm runtime is not detected on your system.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
const { backend, cpuCapabilities, noavx2, failsafe, availableBackends } =
|
||||||
|
params;
|
||||||
|
|
||||||
|
const isGpuBackend = ['cuda', 'rocm', 'vulkan', 'clblast'].includes(
|
||||||
|
backend
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isGpuBackend) {
|
||||||
|
try {
|
||||||
|
const gpuMemoryInfo =
|
||||||
|
await window.electronAPI.kobold.detectGPUMemory();
|
||||||
|
const lowVramGpus = gpuMemoryInfo.filter(
|
||||||
|
(gpu) =>
|
||||||
|
typeof gpu.totalMemoryMB === 'number' && gpu.totalMemoryMB < 8192
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lowVramGpus.length > 0) {
|
||||||
|
warnings.push({
|
||||||
|
type: 'warning',
|
||||||
|
message: `Low VRAM detected (${lowVramGpus
|
||||||
|
.map(
|
||||||
|
(gpu) =>
|
||||||
|
`${gpu.deviceName}: ${(gpu.totalMemoryMB! / 1024).toFixed(1)}GB`
|
||||||
|
)
|
||||||
|
.join(
|
||||||
|
', '
|
||||||
|
)}). Consider using smaller models, reducing GPU layers, or enabling the "Low VRAM" option on the Advanced tab.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
window.electronAPI.logs.logError(
|
||||||
|
'Failed to detect GPU memory:',
|
||||||
|
error as Error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backend === 'cpu' && cpuCapabilities) {
|
||||||
|
if (!cpuCapabilities.avx2 && !noavx2) {
|
||||||
|
warnings.push({
|
||||||
|
type: 'warning',
|
||||||
|
message:
|
||||||
|
'Your CPU does not support AVX2. Enable the "Disable AVX2" option on the Advanced tab to avoid crashes.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cpuCapabilities.avx && !cpuCapabilities.avx2 && !failsafe) {
|
||||||
|
warnings.push({
|
||||||
|
type: 'warning',
|
||||||
|
message:
|
||||||
|
'Your CPU does not support AVX or AVX2. Enable the "Failsafe" option on the Advanced tab to avoid crashes.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
availableBackends.length > 0 &&
|
||||||
|
availableBackends.some((b) => b.value === 'cpu')
|
||||||
|
) {
|
||||||
|
warnings.push({
|
||||||
|
type: 'info',
|
||||||
|
message:
|
||||||
|
"LLMs run significantly faster on GPU-accelerated systems. Consider using NVIDIA's CUDA, AMD's ROCm or Vulkan backends for optimal performance.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return warnings;
|
||||||
|
} catch (error) {
|
||||||
|
window.electronAPI.logs.logError(
|
||||||
|
'Failed to check backend warnings:',
|
||||||
|
error as Error
|
||||||
|
);
|
||||||
|
return warnings;
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Add table
Reference in a new issue