windows fixes of linux fixes of windows fixes

This commit is contained in:
lone-cloud 2025-08-21 00:14:47 -07:00
parent 9719377ae8
commit 7c2ce9c8e0
8 changed files with 159 additions and 128 deletions

View file

@ -40,34 +40,19 @@ export const BackendSelector = ({
useEffect(() => { useEffect(() => {
const loadBackends = async () => { const loadBackends = async () => {
try { try {
const [currentBinaryInfo, cpuCapabilitiesResult, gpuCapabilities] = const [cpuCapabilitiesResult, backends] = await Promise.all([
await Promise.all([ window.electronAPI.kobold.detectCPU(),
window.electronAPI.kobold.getCurrentBinaryInfo(), window.electronAPI.kobold.getAvailableBackends(),
window.electronAPI.kobold.detectCPU(), ]);
window.electronAPI.kobold.detectGPUCapabilities(),
]);
setCpuCapabilities({ setCpuCapabilities({
avx: cpuCapabilitiesResult.avx, avx: cpuCapabilitiesResult.avx,
avx2: cpuCapabilitiesResult.avx2, avx2: cpuCapabilitiesResult.avx2,
}); });
let backends: Array<{ const cpuBackend = backends.find((b) => b.value === 'cpu');
value: string; if (cpuBackend) {
label: string; cpuBackend.devices = cpuCapabilitiesResult.devices;
devices?: string[];
}> = [];
if (currentBinaryInfo?.path) {
backends = await window.electronAPI.kobold.getAvailableBackends(
currentBinaryInfo.path,
gpuCapabilities
);
const cpuBackend = backends.find((b) => b.value === 'cpu');
if (cpuBackend) {
cpuBackend.devices = cpuCapabilitiesResult.devices;
}
} }
setAvailableBackends(backends); setAvailableBackends(backends);
@ -231,7 +216,7 @@ export const BackendSelector = ({
step={1} step={1}
size="sm" size="sm"
w={80} w={80}
disabled={autoGpuLayers} disabled={autoGpuLayers || backend === 'cpu'}
/> />
<Group gap="xs" align="center"> <Group gap="xs" align="center">
<Checkbox <Checkbox
@ -241,6 +226,7 @@ export const BackendSelector = ({
handleAutoGpuLayersChange(event.currentTarget.checked) handleAutoGpuLayersChange(event.currentTarget.checked)
} }
size="sm" size="sm"
disabled={backend === 'cpu'}
/> />
<InfoTooltip label="Automatically try to allocate the GPU layers based on available VRAM." /> <InfoTooltip label="Automatically try to allocate the GPU layers based on available VRAM." />
</Group> </Group>

View file

@ -76,10 +76,7 @@ export const LaunchScreen = ({
const setHappyDefaults = useCallback(async () => { const setHappyDefaults = useCallback(async () => {
try { try {
const backends = await window.electronAPI.kobold.getAvailableBackends( const backends = await window.electronAPI.kobold.getAvailableBackends();
(await window.electronAPI.kobold.getCurrentBinaryInfo())?.path || '',
await window.electronAPI.kobold.detectGPUCapabilities()
);
if (!backend && backends.length > 0) { if (!backend && backends.length > 0) {
handleBackendChange(backends[0].value); handleBackendChange(backends[0].value);

View file

@ -35,7 +35,6 @@ class FriendlyKoboldApp {
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.binaryService = new BinaryService(this.logManager);
this.koboldManager = new KoboldCppManager( this.koboldManager = new KoboldCppManager(
this.configManager, this.configManager,
@ -44,6 +43,12 @@ class FriendlyKoboldApp {
this.logManager this.logManager
); );
this.binaryService = new BinaryService(
this.logManager,
this.koboldManager,
this.hardwareService
);
this.ipcHandlers = new IPCHandlers( this.ipcHandlers = new IPCHandlers(
this.koboldManager, this.koboldManager,
this.configManager, this.configManager,

View file

@ -1,6 +1,8 @@
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import { join, dirname } from 'path'; import { join, dirname } from 'path';
import { LogManager } from '@/main/managers/LogManager'; import { LogManager } from '@/main/managers/LogManager';
import type { KoboldCppManager } from '@/main/managers/KoboldCppManager';
import type { HardwareService } from '@/main/services/HardwareService';
export interface BackendSupport { export interface BackendSupport {
rocm: boolean; rocm: boolean;
@ -18,9 +20,17 @@ export class BinaryService {
Array<{ value: string; label: string; devices?: string[] }> Array<{ value: string; label: string; devices?: string[] }>
>(); >();
private logManager: LogManager; private logManager: LogManager;
private koboldManager: KoboldCppManager;
private hardwareService: HardwareService;
constructor(logManager: LogManager) { constructor(
logManager: LogManager,
koboldManager: KoboldCppManager,
hardwareService: HardwareService
) {
this.logManager = logManager; this.logManager = logManager;
this.koboldManager = koboldManager;
this.hardwareService = hardwareService;
} }
detectBackendSupport(koboldBinaryPath: string): BackendSupport { detectBackendSupport(koboldBinaryPath: string): BackendSupport {
@ -46,7 +56,18 @@ export class BinaryService {
const hasKoboldCppLib = (name: string) => { const hasKoboldCppLib = (name: string) => {
const filename = `${name}${libExtension}`; const filename = `${name}${libExtension}`;
return existsSync(join(internalDir, filename));
if (platform === 'win32') {
return (
existsSync(join(binaryDir, filename)) ||
existsSync(join(internalDir, filename))
);
} else {
return (
existsSync(join(internalDir, filename)) ||
existsSync(join(binaryDir, filename))
);
}
}; };
support.rocm = hasKoboldCppLib('koboldcpp_hipblas'); support.rocm = hasKoboldCppLib('koboldcpp_hipblas');
@ -66,67 +87,78 @@ export class BinaryService {
return support; return support;
} }
getAvailableBackends( async getAvailableBackends(): Promise<
koboldBinaryPath: string, Array<{ value: string; label: string; devices?: string[] }>
hardwareCapabilities?: { > {
cuda: { supported: boolean; devices: string[] }; try {
rocm: { supported: boolean; devices: string[] }; const [currentBinaryInfo, hardwareCapabilities] = await Promise.all([
vulkan: { supported: boolean; devices: string[] }; this.koboldManager.getCurrentBinaryInfo(),
clblast: { supported: boolean; devices: string[] }; this.hardwareService.detectGPUCapabilities(),
} ]);
): Array<{ value: string; label: string; devices?: string[] }> {
const cacheKey = `${koboldBinaryPath}:${JSON.stringify(hardwareCapabilities)}`;
if (this.availableBackendsCache.has(cacheKey)) { if (!currentBinaryInfo?.path) {
return this.availableBackendsCache.get(cacheKey)!; return [{ value: 'cpu', label: 'CPU' }];
} }
const backendSupport = this.detectBackendSupport(koboldBinaryPath); const cacheKey = `${currentBinaryInfo.path}:${JSON.stringify(hardwareCapabilities)}`;
const backends: Array<{
value: string; if (this.availableBackendsCache.has(cacheKey)) {
label: string; return this.availableBackendsCache.get(cacheKey)!;
devices?: string[]; }
}> = [];
const backendSupport = this.detectBackendSupport(currentBinaryInfo.path);
const backends: Array<{
value: string;
label: string;
devices?: string[];
}> = [];
if (backendSupport.cuda && hardwareCapabilities.cuda.supported) {
backends.push({
value: 'cuda',
label: 'CUDA',
devices: hardwareCapabilities.cuda.devices,
});
}
if (backendSupport.rocm && hardwareCapabilities.rocm.supported) {
backends.push({
value: 'rocm',
label: 'ROCm',
devices: hardwareCapabilities.rocm.devices,
});
}
if (backendSupport.vulkan && hardwareCapabilities.vulkan.supported) {
backends.push({
value: 'vulkan',
label: 'Vulkan',
devices: hardwareCapabilities.vulkan.devices,
});
}
if (backendSupport.clblast && hardwareCapabilities.clblast.supported) {
backends.push({
value: 'clblast',
label: 'CLBlast',
devices: hardwareCapabilities.clblast.devices,
});
}
if (backendSupport.cuda && hardwareCapabilities?.cuda.supported) {
backends.push({ backends.push({
value: 'cuda', value: 'cpu',
label: 'CUDA', label: 'CPU',
devices: hardwareCapabilities.cuda.devices,
}); });
this.availableBackendsCache.set(cacheKey, backends);
return backends;
} catch (error) {
this.logManager.logError(
'Failed to get available backends:',
error as Error
);
return [{ value: 'cpu', label: 'CPU' }];
} }
if (backendSupport.rocm && hardwareCapabilities?.rocm.supported) {
backends.push({
value: 'rocm',
label: 'ROCm',
devices: hardwareCapabilities.rocm.devices,
});
}
if (backendSupport.vulkan && hardwareCapabilities?.vulkan.supported) {
backends.push({
value: 'vulkan',
label: 'Vulkan',
devices: hardwareCapabilities.vulkan.devices,
});
}
if (backendSupport.clblast && hardwareCapabilities?.clblast.supported) {
backends.push({
value: 'clblast',
label: 'CLBlast',
devices: hardwareCapabilities.clblast.devices,
});
}
backends.push({
value: 'cpu',
label: 'CPU',
});
this.availableBackendsCache.set(cacheKey, backends);
return backends;
} }
clearCache(): void { clearCache(): void {

View file

@ -125,6 +125,7 @@ export class HardwareService {
]); ]);
this.gpuCapabilitiesCache = { cuda, rocm, vulkan, clblast }; this.gpuCapabilitiesCache = { cuda, rocm, vulkan, clblast };
return this.gpuCapabilitiesCache; return this.gpuCapabilitiesCache;
} }
@ -303,10 +304,14 @@ export class HardwareService {
const lines = output.split('\n'); const lines = output.split('\n');
for (const line of lines) { for (const line of lines) {
if (line.includes('deviceName')) { // Handle both formats: "deviceName = AMD Radeon RX 7900 GRE" and other potential formats
const name = line.split('=')[1]?.trim(); if (line.includes('deviceName') && line.includes('=')) {
if (name) { const parts = line.split('=');
devices.push(shortenDeviceName(name)); if (parts.length >= 2) {
const name = parts[1]?.trim();
if (name) {
devices.push(shortenDeviceName(name));
}
} }
} }
} }
@ -343,29 +348,23 @@ export class HardwareService {
const lines = output.split('\n'); const lines = output.split('\n');
let currentPlatform = ''; let currentPlatform = '';
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Extract platform name for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
// Extract platform name - this appears early in the output
if (line.includes('Platform Name:')) { if (line.includes('Platform Name:')) {
currentPlatform = line.split('Platform Name:')[1]?.trim() || ''; currentPlatform = line.split('Platform Name:')[1]?.trim() || '';
continue; continue;
} }
// Extract device names for GPU devices // When we find a GPU device type, look for device name
if (line.includes('Device Name:')) { if (line.includes('Device Type:') && line.includes('GPU')) {
const deviceName = line.split('Device Name:')[1]?.trim(); const deviceName = this.findDeviceNameInClInfo(lines, i);
if (deviceName) {
// Look ahead to check if this is a GPU device if (deviceName && currentPlatform) {
for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) { const deviceLabel = `${shortenDeviceName(deviceName)} (${currentPlatform})`;
if (lines[j].includes('Device Type:') && lines[j].includes('GPU')) { devices.push(deviceLabel);
const deviceLabel = currentPlatform
? `${shortenDeviceName(deviceName)} (${currentPlatform})`
: shortenDeviceName(deviceName);
devices.push(deviceLabel);
break;
}
}
} }
} }
} }
@ -373,6 +372,34 @@ export class HardwareService {
return devices; return devices;
} }
private findDeviceNameInClInfo(lines: string[], startIndex: number): string {
// Look for Board name first (appears closer to Device Type and is more descriptive)
for (
let j = startIndex + 1;
j < Math.min(startIndex + 50, lines.length);
j++
) {
const nextLine = lines[j].trim();
if (nextLine.includes('Board name:')) {
return nextLine.split('Board name:')[1]?.trim() || '';
}
}
// If no Board name found, look for Name: field (appears much later)
for (
let j = startIndex + 1;
j < Math.min(startIndex + 100, lines.length);
j++
) {
const nextLine = lines[j].trim();
if (nextLine.startsWith('Name:')) {
return nextLine.split('Name:')[1]?.trim() || '';
}
}
return '';
}
private async detectCLBlast(): Promise<{ private async detectCLBlast(): Promise<{
supported: boolean; supported: boolean;
devices: string[]; devices: string[];

View file

@ -6,7 +6,6 @@ import { LogManager } from '@/main/managers/LogManager';
import { GitHubService } from '@/main/services/GitHubService'; import { GitHubService } from '@/main/services/GitHubService';
import { HardwareService } from '@/main/services/HardwareService'; import { HardwareService } from '@/main/services/HardwareService';
import { BinaryService } from '@/main/services/BinaryService'; import { BinaryService } from '@/main/services/BinaryService';
import type { GPUCapabilities } from '@/types/hardware';
export class IPCHandlers { export class IPCHandlers {
private koboldManager: KoboldCppManager; private koboldManager: KoboldCppManager;
@ -125,13 +124,8 @@ export class IPCHandlers {
this.binaryService.detectBackendSupport(binaryPath) this.binaryService.detectBackendSupport(binaryPath)
); );
ipcMain.handle( ipcMain.handle('kobold:getAvailableBackends', () =>
'kobold:getAvailableBackends', this.binaryService.getAvailableBackends()
(_, binaryPath: string, hardwareCapabilities: GPUCapabilities) =>
this.binaryService.getAvailableBackends(
binaryPath,
hardwareCapabilities
)
); );
ipcMain.handle('kobold:getPlatform', () => ({ ipcMain.handle('kobold:getPlatform', () => ({

View file

@ -6,7 +6,6 @@ import type {
LogsAPI, LogsAPI,
UpdateInfo, UpdateInfo,
} from '@/types/electron'; } from '@/types/electron';
import type { GPUCapabilities } from '@/types/hardware';
const koboldAPI: KoboldAPI = { const koboldAPI: KoboldAPI = {
getInstalledVersion: () => ipcRenderer.invoke('kobold:getInstalledVersion'), getInstalledVersion: () => ipcRenderer.invoke('kobold:getInstalledVersion'),
@ -32,15 +31,7 @@ const koboldAPI: KoboldAPI = {
ipcRenderer.invoke('kobold:detectAllCapabilities'), ipcRenderer.invoke('kobold:detectAllCapabilities'),
detectBackendSupport: (binaryPath: string) => detectBackendSupport: (binaryPath: string) =>
ipcRenderer.invoke('kobold:detectBackendSupport', binaryPath), ipcRenderer.invoke('kobold:detectBackendSupport', binaryPath),
getAvailableBackends: ( getAvailableBackends: () => ipcRenderer.invoke('kobold:getAvailableBackends'),
binaryPath: string,
hardwareCapabilities: GPUCapabilities
) =>
ipcRenderer.invoke(
'kobold:getAvailableBackends',
binaryPath,
hardwareCapabilities
),
getCurrentInstallDir: () => ipcRenderer.invoke('kobold:getCurrentInstallDir'), getCurrentInstallDir: () => ipcRenderer.invoke('kobold:getCurrentInstallDir'),
selectInstallDirectory: () => selectInstallDirectory: () =>
ipcRenderer.invoke('kobold:selectInstallDirectory'), ipcRenderer.invoke('kobold:selectInstallDirectory'),

View file

@ -81,10 +81,9 @@ export interface KoboldAPI {
failsafe: boolean; failsafe: boolean;
cuda: boolean; cuda: boolean;
}>; }>;
getAvailableBackends: ( getAvailableBackends: () => Promise<
binaryPath: string, Array<{ value: string; label: string; devices?: string[] }>
hardwareCapabilities: GPUCapabilities >;
) => Promise<Array<{ value: string; label: string; devices?: string[] }>>;
getCurrentInstallDir: () => Promise<string>; getCurrentInstallDir: () => Promise<string>;
selectInstallDirectory: () => Promise<string | null>; selectInstallDirectory: () => Promise<string | null>;
downloadRelease: ( downloadRelease: (