mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-04 12:13:28 -07:00
174 lines
5.5 KiB
TypeScript
174 lines
5.5 KiB
TypeScript
import { Text, Group, Select, Checkbox, TextInput } from '@mantine/core';
|
|
import { useState, useEffect, useRef } from 'react';
|
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
|
import { BackendSelectItem } from '@/components/screens/Launch/GeneralTab/BackendSelectItem';
|
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
|
|
|
interface BackendSelectorProps {
|
|
onBackendsReady?: () => void;
|
|
}
|
|
|
|
export const BackendSelector = ({ onBackendsReady }: BackendSelectorProps) => {
|
|
const {
|
|
backend,
|
|
gpuDevice,
|
|
gpuLayers,
|
|
autoGpuLayers,
|
|
handleBackendChange,
|
|
handleGpuDeviceChange,
|
|
handleGpuLayersChange,
|
|
handleAutoGpuLayersChange,
|
|
} = useLaunchConfig();
|
|
|
|
const [availableBackends, setAvailableBackends] = useState<
|
|
Array<{ value: string; label: string; devices?: string[] }>
|
|
>([]);
|
|
const hasInitialized = useRef(false);
|
|
|
|
useEffect(() => {
|
|
const loadBackends = async () => {
|
|
try {
|
|
const [cpuCapabilitiesResult, backends] = await Promise.all([
|
|
window.electronAPI.kobold.detectCPU(),
|
|
window.electronAPI.kobold.getAvailableBackends(),
|
|
]);
|
|
|
|
const cpuBackend = backends.find((b) => b.value === 'cpu');
|
|
if (cpuBackend) {
|
|
cpuBackend.devices = cpuCapabilitiesResult.devices;
|
|
}
|
|
|
|
setAvailableBackends(backends);
|
|
hasInitialized.current = true;
|
|
|
|
if (onBackendsReady) {
|
|
onBackendsReady();
|
|
}
|
|
} catch (error) {
|
|
window.electronAPI.logs.logError(
|
|
'Failed to detect available backends:',
|
|
error as Error
|
|
);
|
|
setAvailableBackends([]);
|
|
|
|
if (onBackendsReady) {
|
|
onBackendsReady();
|
|
}
|
|
}
|
|
};
|
|
|
|
if (!hasInitialized.current) {
|
|
loadBackends();
|
|
}
|
|
|
|
const cleanup = window.electronAPI.kobold.onVersionsUpdated(() => {
|
|
hasInitialized.current = false;
|
|
loadBackends();
|
|
});
|
|
|
|
return cleanup;
|
|
}, [onBackendsReady]);
|
|
|
|
return (
|
|
<div>
|
|
<Group justify="space-between" align="flex-start" mb="xs">
|
|
<div style={{ flex: 1, marginRight: '2rem' }}>
|
|
<Group gap="xs" align="center" mb="xs">
|
|
<Text size="sm" fw={500}>
|
|
Backend
|
|
</Text>
|
|
<InfoTooltip label="Select a backend to use. CUDA runs on NVIDIA GPUs, and is much faster. ROCm is the AMD equivalent. Vulkan and CLBlast work on all GPUs." />
|
|
</Group>
|
|
<Select
|
|
placeholder="Select backend"
|
|
value={backend}
|
|
onChange={(value) => {
|
|
if (value) {
|
|
handleBackendChange(value);
|
|
}
|
|
}}
|
|
data={availableBackends.map((b) => ({
|
|
value: b.value,
|
|
label: b.label,
|
|
}))}
|
|
disabled={availableBackends.length === 0}
|
|
renderOption={({ option }) => {
|
|
const backendData = availableBackends.find(
|
|
(b) => b.value === option.value
|
|
);
|
|
return (
|
|
<BackendSelectItem
|
|
label={backendData?.label || option.label.split(' (')[0]}
|
|
devices={backendData?.devices}
|
|
/>
|
|
);
|
|
}}
|
|
/>
|
|
{(() => {
|
|
const selectedBackend = availableBackends.find(
|
|
(b) => b.value === backend
|
|
);
|
|
const isGpuBackend = backend === 'cuda' || backend === 'rocm';
|
|
const hasMultipleDevices =
|
|
selectedBackend?.devices && selectedBackend.devices.length > 1;
|
|
|
|
return (
|
|
isGpuBackend &&
|
|
hasMultipleDevices && (
|
|
<Select
|
|
label="GPU Device"
|
|
placeholder="Select GPU device"
|
|
value={gpuDevice.toString()}
|
|
onChange={(value) =>
|
|
value && handleGpuDeviceChange(parseInt(value, 10))
|
|
}
|
|
data={selectedBackend.devices!.map((device, index) => ({
|
|
value: index.toString(),
|
|
label: `GPU ${index}: ${device}`,
|
|
}))}
|
|
mt="xs"
|
|
/>
|
|
)
|
|
);
|
|
})()}
|
|
</div>
|
|
|
|
<div style={{ flex: 1 }}>
|
|
<Group gap="xs" align="center" mb="xs">
|
|
<Text size="sm" fw={500}>
|
|
GPU Layers
|
|
</Text>
|
|
<InfoTooltip label="The number of layers to offload to your GPU's VRAM. Ideally the entire LLM should fit inside the VRAM for optimal performance." />
|
|
</Group>
|
|
<Group gap="lg" align="center">
|
|
<TextInput
|
|
value={gpuLayers.toString()}
|
|
onChange={(event) =>
|
|
handleGpuLayersChange(Number(event.target.value) || 0)
|
|
}
|
|
type="number"
|
|
min={0}
|
|
max={100}
|
|
step={1}
|
|
size="sm"
|
|
w={80}
|
|
disabled={autoGpuLayers || backend === 'cpu'}
|
|
/>
|
|
<Group gap="xs" align="center">
|
|
<Checkbox
|
|
label="Auto"
|
|
checked={autoGpuLayers}
|
|
onChange={(event) =>
|
|
handleAutoGpuLayersChange(event.currentTarget.checked)
|
|
}
|
|
size="sm"
|
|
disabled={backend === 'cpu'}
|
|
/>
|
|
<InfoTooltip label="Automatically try to allocate the GPU layers based on available VRAM." />
|
|
</Group>
|
|
</Group>
|
|
</div>
|
|
</Group>
|
|
</div>
|
|
);
|
|
};
|