diff --git a/src/components/screens/Launch/GeneralTab/BackendSelector.tsx b/src/components/screens/Launch/GeneralTab/BackendSelector.tsx
index 2334500..8172f68 100644
--- a/src/components/screens/Launch/GeneralTab/BackendSelector.tsx
+++ b/src/components/screens/Launch/GeneralTab/BackendSelector.tsx
@@ -40,34 +40,19 @@ export const BackendSelector = ({
useEffect(() => {
const loadBackends = async () => {
try {
- const [currentBinaryInfo, cpuCapabilitiesResult, gpuCapabilities] =
- await Promise.all([
- window.electronAPI.kobold.getCurrentBinaryInfo(),
- window.electronAPI.kobold.detectCPU(),
- window.electronAPI.kobold.detectGPUCapabilities(),
- ]);
+ const [cpuCapabilitiesResult, backends] = await Promise.all([
+ window.electronAPI.kobold.detectCPU(),
+ window.electronAPI.kobold.getAvailableBackends(),
+ ]);
setCpuCapabilities({
avx: cpuCapabilitiesResult.avx,
avx2: cpuCapabilitiesResult.avx2,
});
- let backends: Array<{
- value: string;
- label: string;
- 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;
- }
+ const cpuBackend = backends.find((b) => b.value === 'cpu');
+ if (cpuBackend) {
+ cpuBackend.devices = cpuCapabilitiesResult.devices;
}
setAvailableBackends(backends);
@@ -231,7 +216,7 @@ export const BackendSelector = ({
step={1}
size="sm"
w={80}
- disabled={autoGpuLayers}
+ disabled={autoGpuLayers || backend === 'cpu'}
/>
diff --git a/src/components/screens/Launch/index.tsx b/src/components/screens/Launch/index.tsx
index 28c3043..bcae641 100644
--- a/src/components/screens/Launch/index.tsx
+++ b/src/components/screens/Launch/index.tsx
@@ -76,10 +76,7 @@ export const LaunchScreen = ({
const setHappyDefaults = useCallback(async () => {
try {
- const backends = await window.electronAPI.kobold.getAvailableBackends(
- (await window.electronAPI.kobold.getCurrentBinaryInfo())?.path || '',
- await window.electronAPI.kobold.detectGPUCapabilities()
- );
+ const backends = await window.electronAPI.kobold.getAvailableBackends();
if (!backend && backends.length > 0) {
handleBackendChange(backends[0].value);
diff --git a/src/main/index.ts b/src/main/index.ts
index ac3e918..e5350d7 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -35,7 +35,6 @@ class FriendlyKoboldApp {
this.windowManager = new WindowManager();
this.githubService = new GitHubService(this.logManager);
this.hardwareService = new HardwareService();
- this.binaryService = new BinaryService(this.logManager);
this.koboldManager = new KoboldCppManager(
this.configManager,
@@ -44,6 +43,12 @@ class FriendlyKoboldApp {
this.logManager
);
+ this.binaryService = new BinaryService(
+ this.logManager,
+ this.koboldManager,
+ this.hardwareService
+ );
+
this.ipcHandlers = new IPCHandlers(
this.koboldManager,
this.configManager,
diff --git a/src/main/services/BinaryService.ts b/src/main/services/BinaryService.ts
index cd27af6..10aba70 100644
--- a/src/main/services/BinaryService.ts
+++ b/src/main/services/BinaryService.ts
@@ -1,6 +1,8 @@
import { existsSync } from 'fs';
import { join, dirname } from 'path';
import { LogManager } from '@/main/managers/LogManager';
+import type { KoboldCppManager } from '@/main/managers/KoboldCppManager';
+import type { HardwareService } from '@/main/services/HardwareService';
export interface BackendSupport {
rocm: boolean;
@@ -18,9 +20,17 @@ export class BinaryService {
Array<{ value: string; label: string; devices?: string[] }>
>();
private logManager: LogManager;
+ private koboldManager: KoboldCppManager;
+ private hardwareService: HardwareService;
- constructor(logManager: LogManager) {
+ constructor(
+ logManager: LogManager,
+ koboldManager: KoboldCppManager,
+ hardwareService: HardwareService
+ ) {
this.logManager = logManager;
+ this.koboldManager = koboldManager;
+ this.hardwareService = hardwareService;
}
detectBackendSupport(koboldBinaryPath: string): BackendSupport {
@@ -46,7 +56,18 @@ export class BinaryService {
const hasKoboldCppLib = (name: string) => {
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');
@@ -66,67 +87,78 @@ export class BinaryService {
return support;
}
- getAvailableBackends(
- koboldBinaryPath: string,
- hardwareCapabilities?: {
- cuda: { supported: boolean; devices: string[] };
- rocm: { supported: boolean; devices: string[] };
- vulkan: { supported: boolean; devices: string[] };
- clblast: { supported: boolean; devices: string[] };
- }
- ): Array<{ value: string; label: string; devices?: string[] }> {
- const cacheKey = `${koboldBinaryPath}:${JSON.stringify(hardwareCapabilities)}`;
+ async getAvailableBackends(): Promise<
+ Array<{ value: string; label: string; devices?: string[] }>
+ > {
+ try {
+ const [currentBinaryInfo, hardwareCapabilities] = await Promise.all([
+ this.koboldManager.getCurrentBinaryInfo(),
+ this.hardwareService.detectGPUCapabilities(),
+ ]);
- if (this.availableBackendsCache.has(cacheKey)) {
- return this.availableBackendsCache.get(cacheKey)!;
- }
+ if (!currentBinaryInfo?.path) {
+ return [{ value: 'cpu', label: 'CPU' }];
+ }
- const backendSupport = this.detectBackendSupport(koboldBinaryPath);
- const backends: Array<{
- value: string;
- label: string;
- devices?: string[];
- }> = [];
+ const cacheKey = `${currentBinaryInfo.path}:${JSON.stringify(hardwareCapabilities)}`;
+
+ if (this.availableBackendsCache.has(cacheKey)) {
+ return this.availableBackendsCache.get(cacheKey)!;
+ }
+
+ 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({
- value: 'cuda',
- label: 'CUDA',
- devices: hardwareCapabilities.cuda.devices,
+ value: 'cpu',
+ label: 'CPU',
});
+
+ 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 {
diff --git a/src/main/services/HardwareService.ts b/src/main/services/HardwareService.ts
index 6bf0ee8..4ce034b 100644
--- a/src/main/services/HardwareService.ts
+++ b/src/main/services/HardwareService.ts
@@ -125,6 +125,7 @@ export class HardwareService {
]);
this.gpuCapabilitiesCache = { cuda, rocm, vulkan, clblast };
+
return this.gpuCapabilitiesCache;
}
@@ -303,10 +304,14 @@ export class HardwareService {
const lines = output.split('\n');
for (const line of lines) {
- if (line.includes('deviceName')) {
- const name = line.split('=')[1]?.trim();
- if (name) {
- devices.push(shortenDeviceName(name));
+ // Handle both formats: "deviceName = AMD Radeon RX 7900 GRE" and other potential formats
+ if (line.includes('deviceName') && line.includes('=')) {
+ const parts = line.split('=');
+ 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');
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:')) {
currentPlatform = line.split('Platform Name:')[1]?.trim() || '';
continue;
}
- // Extract device names for GPU devices
- if (line.includes('Device Name:')) {
- const deviceName = line.split('Device Name:')[1]?.trim();
- if (deviceName) {
- // Look ahead to check if this is a GPU device
- for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
- if (lines[j].includes('Device Type:') && lines[j].includes('GPU')) {
- const deviceLabel = currentPlatform
- ? `${shortenDeviceName(deviceName)} (${currentPlatform})`
- : shortenDeviceName(deviceName);
- devices.push(deviceLabel);
- break;
- }
- }
+ // When we find a GPU device type, look for device name
+ if (line.includes('Device Type:') && line.includes('GPU')) {
+ const deviceName = this.findDeviceNameInClInfo(lines, i);
+
+ if (deviceName && currentPlatform) {
+ const deviceLabel = `${shortenDeviceName(deviceName)} (${currentPlatform})`;
+ devices.push(deviceLabel);
}
}
}
@@ -373,6 +372,34 @@ export class HardwareService {
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<{
supported: boolean;
devices: string[];
diff --git a/src/main/utils/IPCHandlers.ts b/src/main/utils/IPCHandlers.ts
index 7c4c45a..91d6213 100644
--- a/src/main/utils/IPCHandlers.ts
+++ b/src/main/utils/IPCHandlers.ts
@@ -6,7 +6,6 @@ import { LogManager } from '@/main/managers/LogManager';
import { GitHubService } from '@/main/services/GitHubService';
import { HardwareService } from '@/main/services/HardwareService';
import { BinaryService } from '@/main/services/BinaryService';
-import type { GPUCapabilities } from '@/types/hardware';
export class IPCHandlers {
private koboldManager: KoboldCppManager;
@@ -125,13 +124,8 @@ export class IPCHandlers {
this.binaryService.detectBackendSupport(binaryPath)
);
- ipcMain.handle(
- 'kobold:getAvailableBackends',
- (_, binaryPath: string, hardwareCapabilities: GPUCapabilities) =>
- this.binaryService.getAvailableBackends(
- binaryPath,
- hardwareCapabilities
- )
+ ipcMain.handle('kobold:getAvailableBackends', () =>
+ this.binaryService.getAvailableBackends()
);
ipcMain.handle('kobold:getPlatform', () => ({
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 2b1452f..9c21655 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -6,7 +6,6 @@ import type {
LogsAPI,
UpdateInfo,
} from '@/types/electron';
-import type { GPUCapabilities } from '@/types/hardware';
const koboldAPI: KoboldAPI = {
getInstalledVersion: () => ipcRenderer.invoke('kobold:getInstalledVersion'),
@@ -32,15 +31,7 @@ const koboldAPI: KoboldAPI = {
ipcRenderer.invoke('kobold:detectAllCapabilities'),
detectBackendSupport: (binaryPath: string) =>
ipcRenderer.invoke('kobold:detectBackendSupport', binaryPath),
- getAvailableBackends: (
- binaryPath: string,
- hardwareCapabilities: GPUCapabilities
- ) =>
- ipcRenderer.invoke(
- 'kobold:getAvailableBackends',
- binaryPath,
- hardwareCapabilities
- ),
+ getAvailableBackends: () => ipcRenderer.invoke('kobold:getAvailableBackends'),
getCurrentInstallDir: () => ipcRenderer.invoke('kobold:getCurrentInstallDir'),
selectInstallDirectory: () =>
ipcRenderer.invoke('kobold:selectInstallDirectory'),
diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts
index 43fbb8d..192381d 100644
--- a/src/types/electron.d.ts
+++ b/src/types/electron.d.ts
@@ -81,10 +81,9 @@ export interface KoboldAPI {
failsafe: boolean;
cuda: boolean;
}>;
- getAvailableBackends: (
- binaryPath: string,
- hardwareCapabilities: GPUCapabilities
- ) => Promise>;
+ getAvailableBackends: () => Promise<
+ Array<{ value: string; label: string; devices?: string[] }>
+ >;
getCurrentInstallDir: () => Promise;
selectInstallDirectory: () => Promise;
downloadRelease: (