mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
fixing clblast, more refactoring of slop code, zustand for complex tab state logic
This commit is contained in:
parent
ed4fa55478
commit
1c5bd49e3c
23 changed files with 1779 additions and 1386 deletions
|
|
@ -33,6 +33,7 @@
|
|||
"KOBOLDAI",
|
||||
"koboldcpp",
|
||||
"KoboldCpp",
|
||||
"lora",
|
||||
"lowvram",
|
||||
"Lowvram",
|
||||
"maximizable",
|
||||
|
|
@ -50,6 +51,7 @@
|
|||
"nvidia",
|
||||
"oldpc",
|
||||
"OLDPC",
|
||||
"opencl",
|
||||
"Papi",
|
||||
"Philippov",
|
||||
"pkexec",
|
||||
|
|
|
|||
|
|
@ -82,9 +82,11 @@
|
|||
"@mantine/core": "^8.2.5",
|
||||
"@mantine/hooks": "^8.2.5",
|
||||
"lucide-react": "^0.540.0",
|
||||
"opencl-info": "^0.3.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"systeminformation": "^5.27.7"
|
||||
"systeminformation": "^5.27.7",
|
||||
"zustand": "^5.0.8"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.friendly-kobold.app",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import styles from '@/styles/layout.module.css';
|
|||
interface ConfigFileManagerProps {
|
||||
configFiles: ConfigFile[];
|
||||
selectedFile: string | null;
|
||||
hasUnsavedChanges: boolean;
|
||||
onFileSelection: (fileName: string) => Promise<void>;
|
||||
onCreateNewConfig: (configName: string) => Promise<void>;
|
||||
onSaveConfig: () => void;
|
||||
|
|
@ -64,7 +63,6 @@ SelectItem.displayName = 'SelectItem';
|
|||
export const ConfigFileManager = ({
|
||||
configFiles,
|
||||
selectedFile,
|
||||
hasUnsavedChanges,
|
||||
onFileSelection,
|
||||
onCreateNewConfig,
|
||||
onSaveConfig,
|
||||
|
|
@ -108,15 +106,6 @@ export const ConfigFileManager = ({
|
|||
};
|
||||
});
|
||||
|
||||
if (selectedFile === null && hasUnsavedChanges) {
|
||||
const displayName = 'New Configuration (unsaved)';
|
||||
selectData.unshift({
|
||||
value: '__new__',
|
||||
label: displayName,
|
||||
extension: '.kcpps',
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack gap="xs">
|
||||
|
|
@ -127,11 +116,7 @@ export const ConfigFileManager = ({
|
|||
<div className={styles.flex1}>
|
||||
<Select
|
||||
placeholder="Select a configuration file"
|
||||
value={
|
||||
selectedFile === null && hasUnsavedChanges
|
||||
? '__new__'
|
||||
: selectedFile
|
||||
}
|
||||
value={selectedFile}
|
||||
onChange={(value: string | null) => {
|
||||
if (value === '__new__') {
|
||||
return;
|
||||
|
|
@ -156,24 +141,18 @@ export const ConfigFileManager = ({
|
|||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant={
|
||||
selectedFile === null && hasUnsavedChanges ? 'filled' : 'light'
|
||||
}
|
||||
variant="light"
|
||||
leftSection={<Plus size={14} />}
|
||||
size="sm"
|
||||
disabled={selectedFile === null && hasUnsavedChanges}
|
||||
onClick={() => handleOpenConfigModal()}
|
||||
>
|
||||
{selectedFile === null && hasUnsavedChanges
|
||||
? 'Creating New...'
|
||||
: 'New'}
|
||||
New
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
leftSection={<Save size={14} />}
|
||||
size="sm"
|
||||
disabled={!hasUnsavedChanges}
|
||||
onClick={() => {
|
||||
if (selectedFile) {
|
||||
onSaveConfig();
|
||||
|
|
|
|||
|
|
@ -1,27 +1,11 @@
|
|||
import { Stack, Text, Group, TextInput, Checkbox } from '@mantine/core';
|
||||
import { Stack, Checkbox, Group, Text, TextInput } from '@mantine/core';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
|
||||
interface AdvancedTabProps {
|
||||
additionalArguments: string;
|
||||
noshift: boolean;
|
||||
flashattention: boolean;
|
||||
noavx2: boolean;
|
||||
failsafe: boolean;
|
||||
lowvram: boolean;
|
||||
quantmatmul: boolean;
|
||||
backend: string;
|
||||
onAdditionalArgumentsChange: (args: string) => void;
|
||||
onNoshiftChange: (noshift: boolean) => void;
|
||||
onFlashattentionChange: (flashattention: boolean) => void;
|
||||
onNoavx2Change: (noavx2: boolean) => void;
|
||||
onFailsafeChange: (failsafe: boolean) => void;
|
||||
onLowvramChange: (lowvram: boolean) => void;
|
||||
onQuantmatmulChange: (quantmatmul: boolean) => void;
|
||||
}
|
||||
|
||||
export const AdvancedTab = ({
|
||||
export const AdvancedTab = () => {
|
||||
const {
|
||||
additionalArguments,
|
||||
noshift,
|
||||
flashattention,
|
||||
|
|
@ -30,14 +14,14 @@ export const AdvancedTab = ({
|
|||
lowvram,
|
||||
quantmatmul,
|
||||
backend,
|
||||
onAdditionalArgumentsChange,
|
||||
onNoshiftChange,
|
||||
onFlashattentionChange,
|
||||
onNoavx2Change,
|
||||
onFailsafeChange,
|
||||
onLowvramChange,
|
||||
onQuantmatmulChange,
|
||||
}: AdvancedTabProps) => {
|
||||
handleAdditionalArgumentsChange,
|
||||
handleNoshiftChange,
|
||||
handleFlashattentionChange,
|
||||
handleNoavx2Change,
|
||||
handleFailsafeChange,
|
||||
handleLowvramChange,
|
||||
handleQuantmatmulChange,
|
||||
} = useLaunchConfig();
|
||||
const [backendSupport, setBackendSupport] = useState<{
|
||||
noavx2: boolean;
|
||||
failsafe: boolean;
|
||||
|
|
@ -87,7 +71,7 @@ export const AdvancedTab = ({
|
|||
<Checkbox
|
||||
checked={!noshift}
|
||||
onChange={(event) =>
|
||||
onNoshiftChange(!event.currentTarget.checked)
|
||||
handleNoshiftChange(!event.currentTarget.checked)
|
||||
}
|
||||
label="Context Shift"
|
||||
/>
|
||||
|
|
@ -100,7 +84,7 @@ export const AdvancedTab = ({
|
|||
<Checkbox
|
||||
checked={noshift}
|
||||
onChange={(event) =>
|
||||
onNoshiftChange(event.currentTarget.checked)
|
||||
handleNoshiftChange(event.currentTarget.checked)
|
||||
}
|
||||
label="No Shift"
|
||||
/>
|
||||
|
|
@ -115,7 +99,7 @@ export const AdvancedTab = ({
|
|||
<Checkbox
|
||||
checked={flashattention}
|
||||
onChange={(event) =>
|
||||
onFlashattentionChange(event.currentTarget.checked)
|
||||
handleFlashattentionChange(event.currentTarget.checked)
|
||||
}
|
||||
label="Flash Attention"
|
||||
/>
|
||||
|
|
@ -128,7 +112,7 @@ export const AdvancedTab = ({
|
|||
<Checkbox
|
||||
checked={lowvram}
|
||||
onChange={(event) =>
|
||||
onLowvramChange(event.currentTarget.checked)
|
||||
handleLowvramChange(event.currentTarget.checked)
|
||||
}
|
||||
label="Low VRAM"
|
||||
disabled={backend !== 'cuda' && backend !== 'rocm'}
|
||||
|
|
@ -150,7 +134,7 @@ export const AdvancedTab = ({
|
|||
<Checkbox
|
||||
checked={quantmatmul}
|
||||
onChange={(event) =>
|
||||
onQuantmatmulChange(event.currentTarget.checked)
|
||||
handleQuantmatmulChange(event.currentTarget.checked)
|
||||
}
|
||||
label="QuantMatMul"
|
||||
disabled={backend !== 'cuda' && backend !== 'rocm'}
|
||||
|
|
@ -181,7 +165,7 @@ export const AdvancedTab = ({
|
|||
<Checkbox
|
||||
checked={noavx2}
|
||||
onChange={(event) =>
|
||||
onNoavx2Change(event.currentTarget.checked)
|
||||
handleNoavx2Change(event.currentTarget.checked)
|
||||
}
|
||||
label="Disable AVX2"
|
||||
disabled={isLoading || !backendSupport?.noavx2}
|
||||
|
|
@ -201,7 +185,7 @@ export const AdvancedTab = ({
|
|||
<Checkbox
|
||||
checked={failsafe}
|
||||
onChange={(event) =>
|
||||
onFailsafeChange(event.currentTarget.checked)
|
||||
handleFailsafeChange(event.currentTarget.checked)
|
||||
}
|
||||
label="Failsafe"
|
||||
disabled={isLoading || !backendSupport?.failsafe}
|
||||
|
|
@ -230,7 +214,7 @@ export const AdvancedTab = ({
|
|||
placeholder="Additional command line arguments"
|
||||
value={additionalArguments}
|
||||
onChange={(event) =>
|
||||
onAdditionalArgumentsChange(event.currentTarget.value)
|
||||
handleAdditionalArgumentsChange(event.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,157 +0,0 @@
|
|||
import { Stack, Text, Group, TextInput, Button, Slider } from '@mantine/core';
|
||||
import { File, Search } from 'lucide-react';
|
||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||
import { BackendSelector } from '@/components/screens/Launch/GeneralTab/BackendSelector';
|
||||
import { getInputValidationState } from '@/utils';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
|
||||
interface GeneralTabProps {
|
||||
modelPath: string;
|
||||
gpuLayers: number;
|
||||
autoGpuLayers: boolean;
|
||||
contextSize: number;
|
||||
backend: string;
|
||||
gpuDevice?: number;
|
||||
noavx2: boolean;
|
||||
failsafe: boolean;
|
||||
onModelPathChange: (path: string) => void;
|
||||
onSelectModelFile: () => void;
|
||||
onGpuLayersChange: (layers: number) => void;
|
||||
onAutoGpuLayersChange: (auto: boolean) => void;
|
||||
onContextSizeChange: (size: number) => void;
|
||||
onBackendChange: (backend: string) => void;
|
||||
onGpuDeviceChange?: (device: number) => void;
|
||||
onWarningsChange?: (
|
||||
warnings: Array<{ type: 'warning' | 'info'; message: string }>
|
||||
) => void;
|
||||
onBackendsReady?: () => void;
|
||||
}
|
||||
|
||||
export const GeneralTab = ({
|
||||
modelPath,
|
||||
gpuLayers,
|
||||
autoGpuLayers,
|
||||
contextSize,
|
||||
backend,
|
||||
gpuDevice,
|
||||
noavx2,
|
||||
failsafe,
|
||||
onModelPathChange,
|
||||
onSelectModelFile,
|
||||
onGpuLayersChange,
|
||||
onAutoGpuLayersChange,
|
||||
onContextSizeChange,
|
||||
onBackendChange,
|
||||
onGpuDeviceChange,
|
||||
onWarningsChange,
|
||||
onBackendsReady,
|
||||
}: GeneralTabProps) => {
|
||||
const validationState = getInputValidationState(modelPath);
|
||||
|
||||
const getInputColor = () => {
|
||||
switch (validationState) {
|
||||
case 'valid':
|
||||
return 'green';
|
||||
case 'invalid':
|
||||
return 'red';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const getHelperText = () => {
|
||||
if (!modelPath.trim()) return undefined;
|
||||
|
||||
if (validationState === 'invalid') {
|
||||
return 'Enter a valid URL or file path to the .gguf';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<BackendSelector
|
||||
backend={backend}
|
||||
onBackendChange={onBackendChange}
|
||||
gpuDevice={gpuDevice}
|
||||
onGpuDeviceChange={onGpuDeviceChange}
|
||||
noavx2={noavx2}
|
||||
failsafe={failsafe}
|
||||
onWarningsChange={onWarningsChange}
|
||||
onBackendsReady={onBackendsReady}
|
||||
gpuLayers={gpuLayers}
|
||||
autoGpuLayers={autoGpuLayers}
|
||||
onGpuLayersChange={onGpuLayersChange}
|
||||
onAutoGpuLayersChange={onAutoGpuLayersChange}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={500} mb="xs">
|
||||
Text Model File
|
||||
</Text>
|
||||
<Group gap="xs" align="flex-start">
|
||||
<div className={styles.flex1}>
|
||||
<TextInput
|
||||
placeholder="Select a .gguf model file or enter a direct URL to file"
|
||||
value={modelPath}
|
||||
onChange={(event) => onModelPathChange(event.currentTarget.value)}
|
||||
color={getInputColor()}
|
||||
error={
|
||||
validationState === 'invalid' ? getHelperText() : undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={onSelectModelFile}
|
||||
variant="light"
|
||||
leftSection={<File size={16} />}
|
||||
>
|
||||
Browse
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.electronAPI.app.openExternal(
|
||||
'https://huggingface.co/models?pipeline_tag=text-generation&library=gguf&sort=trending'
|
||||
);
|
||||
}}
|
||||
variant="outline"
|
||||
leftSection={<Search size={16} />}
|
||||
>
|
||||
Search HF
|
||||
</Button>
|
||||
</Group>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Group justify="space-between" align="center" mb="xs">
|
||||
<Group gap="xs" align="center">
|
||||
<Text size="sm" fw={500}>
|
||||
Context Size
|
||||
</Text>
|
||||
<InfoTooltip label="Controls the memory allocated for maximum context size. The larger the context, the larger the required memory." />
|
||||
</Group>
|
||||
<TextInput
|
||||
value={contextSize?.toString() || ''}
|
||||
onChange={(event) =>
|
||||
onContextSizeChange(Number(event.target.value) || 256)
|
||||
}
|
||||
type="number"
|
||||
min={256}
|
||||
max={131072}
|
||||
step={256}
|
||||
size="sm"
|
||||
w={100}
|
||||
/>
|
||||
</Group>
|
||||
<Slider
|
||||
value={contextSize}
|
||||
min={256}
|
||||
max={131072}
|
||||
step={1}
|
||||
onChange={onContextSizeChange}
|
||||
/>
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
|
@ -2,38 +2,32 @@ 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 {
|
||||
backend: string;
|
||||
onBackendChange: (backend: string) => void;
|
||||
gpuDevice?: number;
|
||||
onGpuDeviceChange?: (device: number) => void;
|
||||
noavx2?: boolean;
|
||||
failsafe?: boolean;
|
||||
onWarningsChange?: (
|
||||
warnings: Array<{ type: 'warning' | 'info'; message: string }>
|
||||
) => void;
|
||||
onBackendsReady?: () => void;
|
||||
gpuLayers?: number;
|
||||
autoGpuLayers?: boolean;
|
||||
onGpuLayersChange?: (layers: number) => void;
|
||||
onAutoGpuLayersChange?: (auto: boolean) => void;
|
||||
}
|
||||
|
||||
export const BackendSelector = ({
|
||||
backend,
|
||||
onBackendChange,
|
||||
gpuDevice = 0,
|
||||
onGpuDeviceChange,
|
||||
noavx2 = false,
|
||||
failsafe = false,
|
||||
onWarningsChange,
|
||||
onBackendsReady,
|
||||
gpuLayers = 0,
|
||||
autoGpuLayers = false,
|
||||
onGpuLayersChange,
|
||||
onAutoGpuLayersChange,
|
||||
}: BackendSelectorProps) => {
|
||||
const {
|
||||
backend,
|
||||
gpuDevice,
|
||||
noavx2,
|
||||
failsafe,
|
||||
gpuLayers,
|
||||
autoGpuLayers,
|
||||
handleBackendChange,
|
||||
handleGpuDeviceChange,
|
||||
handleGpuLayersChange,
|
||||
handleAutoGpuLayersChange,
|
||||
} = useLaunchConfig();
|
||||
|
||||
const [availableBackends, setAvailableBackends] = useState<
|
||||
Array<{ value: string; label: string; devices?: string[] }>
|
||||
>([]);
|
||||
|
|
@ -44,10 +38,6 @@ export const BackendSelector = ({
|
|||
const hasInitialized = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasInitialized.current) return;
|
||||
|
||||
let timeoutId: number;
|
||||
|
||||
const loadBackends = async () => {
|
||||
try {
|
||||
const [currentBinaryInfo, cpuCapabilitiesResult, gpuCapabilities] =
|
||||
|
|
@ -83,16 +73,8 @@ export const BackendSelector = ({
|
|||
setAvailableBackends(backends);
|
||||
hasInitialized.current = true;
|
||||
|
||||
if (backends.length > 0 && !backend) {
|
||||
timeoutId = window.setTimeout(() => {
|
||||
onBackendChange(backends[0].value);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
if (onBackendsReady) {
|
||||
window.setTimeout(() => {
|
||||
onBackendsReady();
|
||||
}, 100);
|
||||
}
|
||||
} catch (error) {
|
||||
window.electronAPI.logs.logError(
|
||||
|
|
@ -107,14 +89,10 @@ export const BackendSelector = ({
|
|||
}
|
||||
};
|
||||
|
||||
if (!hasInitialized.current) {
|
||||
loadBackends();
|
||||
|
||||
return () => {
|
||||
if (timeoutId) {
|
||||
window.clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
}, [backend, onBackendChange, onBackendsReady]);
|
||||
}, [onBackendsReady]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onWarningsChange) return;
|
||||
|
|
@ -130,7 +108,7 @@ export const BackendSelector = ({
|
|||
warnings.push({
|
||||
type: 'warning',
|
||||
message:
|
||||
'Your CPU does not support AVX2. Enable the "Disable AVX2" option to avoid crashes.',
|
||||
'Your CPU does not support AVX2. Enable the "Disable AVX2" option on the Advanced tab to avoid crashes.',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +116,7 @@ export const BackendSelector = ({
|
|||
warnings.push({
|
||||
type: 'warning',
|
||||
message:
|
||||
'Your CPU does not support AVX or AVX2. Enable the "Failsafe" option to avoid crashes.',
|
||||
'Your CPU does not support AVX or AVX2. Enable the "Failsafe" option on the Advanced tab to avoid crashes.',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +156,7 @@ export const BackendSelector = ({
|
|||
value={backend}
|
||||
onChange={(value) => {
|
||||
if (value) {
|
||||
onBackendChange(value);
|
||||
handleBackendChange(value);
|
||||
}
|
||||
}}
|
||||
data={availableBackends.map((b) => ({
|
||||
|
|
@ -186,11 +164,6 @@ export const BackendSelector = ({
|
|||
label: b.label,
|
||||
}))}
|
||||
disabled={availableBackends.length === 0}
|
||||
comboboxProps={{
|
||||
middlewares: {
|
||||
flip: false,
|
||||
},
|
||||
}}
|
||||
renderOption={({ option }) => {
|
||||
const backendData = availableBackends.find(
|
||||
(b) => b.value === option.value
|
||||
|
|
@ -213,14 +186,13 @@ export const BackendSelector = ({
|
|||
|
||||
return (
|
||||
isGpuBackend &&
|
||||
onGpuDeviceChange &&
|
||||
hasMultipleDevices && (
|
||||
<Select
|
||||
label="GPU Device"
|
||||
placeholder="Select GPU device"
|
||||
value={gpuDevice.toString()}
|
||||
onChange={(value) =>
|
||||
value && onGpuDeviceChange(parseInt(value, 10))
|
||||
value && handleGpuDeviceChange(parseInt(value, 10))
|
||||
}
|
||||
data={selectedBackend.devices!.map((device, index) => ({
|
||||
value: index.toString(),
|
||||
|
|
@ -233,7 +205,6 @@ export const BackendSelector = ({
|
|||
})()}
|
||||
</div>
|
||||
|
||||
{onGpuLayersChange && onAutoGpuLayersChange && (
|
||||
<div style={{ flex: 1 }}>
|
||||
<Group gap="xs" align="center" mb="xs">
|
||||
<Text size="sm" fw={500}>
|
||||
|
|
@ -245,7 +216,7 @@ export const BackendSelector = ({
|
|||
<TextInput
|
||||
value={gpuLayers.toString()}
|
||||
onChange={(event) =>
|
||||
onGpuLayersChange(Number(event.target.value) || 0)
|
||||
handleGpuLayersChange(Number(event.target.value) || 0)
|
||||
}
|
||||
type="number"
|
||||
min={0}
|
||||
|
|
@ -260,7 +231,7 @@ export const BackendSelector = ({
|
|||
label="Auto"
|
||||
checked={autoGpuLayers}
|
||||
onChange={(event) =>
|
||||
onAutoGpuLayersChange(event.currentTarget.checked)
|
||||
handleAutoGpuLayersChange(event.currentTarget.checked)
|
||||
}
|
||||
size="sm"
|
||||
/>
|
||||
|
|
@ -268,7 +239,6 @@ export const BackendSelector = ({
|
|||
</Group>
|
||||
</Group>
|
||||
</div>
|
||||
)}
|
||||
</Group>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,24 +3,10 @@ import { File, Search } from 'lucide-react';
|
|||
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||
import { BackendSelector } from '@/components/screens/Launch/GeneralTab/BackendSelector';
|
||||
import { getInputValidationState } from '@/utils';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
|
||||
interface GeneralTabProps {
|
||||
modelPath: string;
|
||||
gpuLayers: number;
|
||||
autoGpuLayers: boolean;
|
||||
contextSize: number;
|
||||
backend: string;
|
||||
gpuDevice?: number;
|
||||
noavx2: boolean;
|
||||
failsafe: boolean;
|
||||
onModelPathChange: (path: string) => void;
|
||||
onSelectModelFile: () => void;
|
||||
onGpuLayersChange: (layers: number) => void;
|
||||
onAutoGpuLayersChange: (auto: boolean) => void;
|
||||
onContextSizeChange: (size: number) => void;
|
||||
onBackendChange: (backend: string) => void;
|
||||
onGpuDeviceChange?: (device: number) => void;
|
||||
onWarningsChange?: (
|
||||
warnings: Array<{ type: 'warning' | 'info'; message: string }>
|
||||
) => void;
|
||||
|
|
@ -28,24 +14,17 @@ interface GeneralTabProps {
|
|||
}
|
||||
|
||||
export const GeneralTab = ({
|
||||
modelPath,
|
||||
gpuLayers,
|
||||
autoGpuLayers,
|
||||
contextSize,
|
||||
backend,
|
||||
gpuDevice,
|
||||
noavx2,
|
||||
failsafe,
|
||||
onModelPathChange,
|
||||
onSelectModelFile,
|
||||
onGpuLayersChange,
|
||||
onAutoGpuLayersChange,
|
||||
onContextSizeChange,
|
||||
onBackendChange,
|
||||
onGpuDeviceChange,
|
||||
onWarningsChange,
|
||||
onBackendsReady,
|
||||
}: GeneralTabProps) => {
|
||||
const {
|
||||
modelPath,
|
||||
contextSize,
|
||||
handleModelPathChange,
|
||||
handleSelectModelFile,
|
||||
handleContextSizeChangeWithStep,
|
||||
} = useLaunchConfig();
|
||||
|
||||
const validationState = getInputValidationState(modelPath);
|
||||
|
||||
const getInputColor = () => {
|
||||
|
|
@ -72,18 +51,8 @@ export const GeneralTab = ({
|
|||
return (
|
||||
<Stack gap="md">
|
||||
<BackendSelector
|
||||
backend={backend}
|
||||
onBackendChange={onBackendChange}
|
||||
gpuDevice={gpuDevice}
|
||||
onGpuDeviceChange={onGpuDeviceChange}
|
||||
noavx2={noavx2}
|
||||
failsafe={failsafe}
|
||||
onWarningsChange={onWarningsChange}
|
||||
onBackendsReady={onBackendsReady}
|
||||
gpuLayers={gpuLayers}
|
||||
autoGpuLayers={autoGpuLayers}
|
||||
onGpuLayersChange={onGpuLayersChange}
|
||||
onAutoGpuLayersChange={onAutoGpuLayersChange}
|
||||
/>
|
||||
|
||||
<div>
|
||||
|
|
@ -95,7 +64,7 @@ export const GeneralTab = ({
|
|||
<TextInput
|
||||
placeholder="Select a .gguf model file or enter a direct URL to file"
|
||||
value={modelPath}
|
||||
onChange={(event) => onModelPathChange(event.currentTarget.value)}
|
||||
onChange={(e) => handleModelPathChange(e.target.value)}
|
||||
color={getInputColor()}
|
||||
error={
|
||||
validationState === 'invalid' ? getHelperText() : undefined
|
||||
|
|
@ -103,7 +72,7 @@ export const GeneralTab = ({
|
|||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={onSelectModelFile}
|
||||
onClick={handleSelectModelFile}
|
||||
variant="light"
|
||||
leftSection={<File size={16} />}
|
||||
>
|
||||
|
|
@ -134,7 +103,7 @@ export const GeneralTab = ({
|
|||
<TextInput
|
||||
value={contextSize?.toString() || ''}
|
||||
onChange={(event) =>
|
||||
onContextSizeChange(Number(event.target.value) || 256)
|
||||
handleContextSizeChangeWithStep(Number(event.target.value) || 256)
|
||||
}
|
||||
type="number"
|
||||
min={256}
|
||||
|
|
@ -149,7 +118,7 @@ export const GeneralTab = ({
|
|||
min={256}
|
||||
max={131072}
|
||||
step={1}
|
||||
onChange={onContextSizeChange}
|
||||
onChange={handleContextSizeChangeWithStep}
|
||||
/>
|
||||
</div>
|
||||
</Stack>
|
||||
|
|
|
|||
|
|
@ -3,32 +3,36 @@ import { useState } from 'react';
|
|||
import { File, Search } from 'lucide-react';
|
||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||
import { getInputValidationState, IMAGE_MODEL_PRESETS } from '@/utils';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
|
||||
interface ImageGenerationTabProps {
|
||||
sdmodel: string;
|
||||
sdt5xxl: string;
|
||||
sdclipl: string;
|
||||
sdclipg: string;
|
||||
sdphotomaker: string;
|
||||
sdvae: string;
|
||||
sdlora: string;
|
||||
onSdmodelChange: (path: string) => void;
|
||||
onSelectSdmodelFile: () => void;
|
||||
onSdt5xxlChange: (path: string) => void;
|
||||
onSelectSdt5xxlFile: () => void;
|
||||
onSdcliplChange: (path: string) => void;
|
||||
onSelectSdcliplFile: () => void;
|
||||
onSdclipgChange: (path: string) => void;
|
||||
onSelectSdclipgFile: () => void;
|
||||
onSdphotomakerChange: (path: string) => void;
|
||||
onSelectSdphotomakerFile: () => void;
|
||||
onSdvaeChange: (path: string) => void;
|
||||
onSelectSdvaeFile: () => void;
|
||||
onSdloraChange: (path: string) => void;
|
||||
onSelectSdloraFile: () => void;
|
||||
onApplyPreset: (presetName: string) => void;
|
||||
}
|
||||
export const ImageGenerationTab = () => {
|
||||
const {
|
||||
sdmodel,
|
||||
sdt5xxl,
|
||||
sdclipl,
|
||||
sdclipg,
|
||||
sdphotomaker,
|
||||
sdvae,
|
||||
sdlora,
|
||||
handleSdmodelChange,
|
||||
handleSelectSdmodelFile,
|
||||
handleSdt5xxlChange,
|
||||
handleSelectSdt5xxlFile,
|
||||
handleSdcliplChange,
|
||||
handleSelectSdcliplFile,
|
||||
handleSdclipgChange,
|
||||
handleSelectSdclipgFile,
|
||||
handleSdphotomakerChange,
|
||||
handleSelectSdphotomakerFile,
|
||||
handleSdvaeChange,
|
||||
handleSelectSdvaeFile,
|
||||
handleSdloraChange,
|
||||
handleSelectSdloraFile,
|
||||
handleApplyPreset,
|
||||
} = useLaunchConfig();
|
||||
|
||||
const [selectedPreset, setSelectedPreset] = useState<string | null>(null);
|
||||
|
||||
const ModelField = ({
|
||||
label,
|
||||
|
|
@ -85,7 +89,9 @@ const ModelField = ({
|
|||
value={value}
|
||||
onChange={(event) => onChange(event.currentTarget.value)}
|
||||
color={getInputColor()}
|
||||
error={validationState === 'invalid' ? getHelperText() : undefined}
|
||||
error={
|
||||
validationState === 'invalid' ? getHelperText() : undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
|
|
@ -113,32 +119,6 @@ const ModelField = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const ImageGenerationTab = ({
|
||||
sdmodel,
|
||||
sdt5xxl,
|
||||
sdclipl,
|
||||
sdclipg,
|
||||
sdphotomaker,
|
||||
sdvae,
|
||||
sdlora,
|
||||
onSdmodelChange,
|
||||
onSelectSdmodelFile,
|
||||
onSdt5xxlChange,
|
||||
onSelectSdt5xxlFile,
|
||||
onSdcliplChange,
|
||||
onSelectSdcliplFile,
|
||||
onSdclipgChange,
|
||||
onSelectSdclipgFile,
|
||||
onSdphotomakerChange,
|
||||
onSelectSdphotomakerFile,
|
||||
onSdvaeChange,
|
||||
onSelectSdvaeFile,
|
||||
onSdloraChange,
|
||||
onSelectSdloraFile,
|
||||
onApplyPreset,
|
||||
}: ImageGenerationTabProps) => {
|
||||
const [selectedPreset, setSelectedPreset] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<div>
|
||||
|
|
@ -158,7 +138,7 @@ export const ImageGenerationTab = ({
|
|||
onChange={(value) => {
|
||||
setSelectedPreset(value);
|
||||
if (value) {
|
||||
onApplyPreset(value);
|
||||
handleApplyPreset(value);
|
||||
}
|
||||
}}
|
||||
clearable
|
||||
|
|
@ -169,8 +149,8 @@ export const ImageGenerationTab = ({
|
|||
label="Image Gen. Model File"
|
||||
value={sdmodel}
|
||||
placeholder="Select a model file or enter a direct URL"
|
||||
onChange={onSdmodelChange}
|
||||
onSelectFile={onSelectSdmodelFile}
|
||||
onChange={handleSdmodelChange}
|
||||
onSelectFile={handleSelectSdmodelFile}
|
||||
showSearchHF
|
||||
/>
|
||||
|
||||
|
|
@ -178,24 +158,24 @@ export const ImageGenerationTab = ({
|
|||
label="T5-XXL File"
|
||||
value={sdt5xxl}
|
||||
placeholder="Select a T5-XXL file or enter a direct URL"
|
||||
onChange={onSdt5xxlChange}
|
||||
onSelectFile={onSelectSdt5xxlFile}
|
||||
onChange={handleSdt5xxlChange}
|
||||
onSelectFile={handleSelectSdt5xxlFile}
|
||||
/>
|
||||
|
||||
<ModelField
|
||||
label="Clip-L File"
|
||||
value={sdclipl}
|
||||
placeholder="Select a Clip-L file or enter a direct URL"
|
||||
onChange={onSdcliplChange}
|
||||
onSelectFile={onSelectSdcliplFile}
|
||||
onChange={handleSdcliplChange}
|
||||
onSelectFile={handleSelectSdcliplFile}
|
||||
/>
|
||||
|
||||
<ModelField
|
||||
label="Clip-G File"
|
||||
value={sdclipg}
|
||||
placeholder="Select a Clip-G file or enter a direct URL"
|
||||
onChange={onSdclipgChange}
|
||||
onSelectFile={onSelectSdclipgFile}
|
||||
onChange={handleSdclipgChange}
|
||||
onSelectFile={handleSelectSdclipgFile}
|
||||
/>
|
||||
|
||||
<ModelField
|
||||
|
|
@ -203,16 +183,16 @@ export const ImageGenerationTab = ({
|
|||
value={sdphotomaker}
|
||||
placeholder="Select a PhotoMaker file or enter a direct URL"
|
||||
tooltip="PhotoMaker is a model that allows face cloning. Select a .safetensors PhotoMaker file to be loaded (SDXL only)."
|
||||
onChange={onSdphotomakerChange}
|
||||
onSelectFile={onSelectSdphotomakerFile}
|
||||
onChange={handleSdphotomakerChange}
|
||||
onSelectFile={handleSelectSdphotomakerFile}
|
||||
/>
|
||||
|
||||
<ModelField
|
||||
label="Image VAE"
|
||||
value={sdvae}
|
||||
placeholder="Select a VAE file or enter a direct URL"
|
||||
onChange={onSdvaeChange}
|
||||
onSelectFile={onSelectSdvaeFile}
|
||||
onChange={handleSdvaeChange}
|
||||
onSelectFile={handleSelectSdvaeFile}
|
||||
/>
|
||||
|
||||
<ModelField
|
||||
|
|
@ -220,8 +200,8 @@ export const ImageGenerationTab = ({
|
|||
value={sdlora}
|
||||
placeholder="Select a LoRa file or enter a direct URL"
|
||||
tooltip="LoRa (Low-Rank Adaptation) file for customizing image generation. Select a .safetensors or .gguf LoRa file to be loaded. Should be unquantized."
|
||||
onChange={onSdloraChange}
|
||||
onSelectFile={onSelectSdloraFile}
|
||||
onChange={handleSdloraChange}
|
||||
onSelectFile={handleSelectSdloraFile}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,26 +1,11 @@
|
|||
import { Stack, Text, TextInput, Group, Checkbox } from '@mantine/core';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
|
||||
interface NetworkTabProps {
|
||||
port: number | undefined;
|
||||
host: string;
|
||||
multiuser: boolean;
|
||||
multiplayer: boolean;
|
||||
remotetunnel: boolean;
|
||||
nocertify: boolean;
|
||||
websearch: boolean;
|
||||
onPortChange: (port: number | undefined) => void;
|
||||
onHostChange: (host: string) => void;
|
||||
onMultiuserChange: (multiuser: boolean) => void;
|
||||
onMultiplayerChange: (multiplayer: boolean) => void;
|
||||
onRemotetunnelChange: (remotetunnel: boolean) => void;
|
||||
onNocertifyChange: (nocertify: boolean) => void;
|
||||
onWebsearchChange: (websearch: boolean) => void;
|
||||
}
|
||||
|
||||
export const NetworkTab = ({
|
||||
export const NetworkTab = () => {
|
||||
const {
|
||||
port,
|
||||
host,
|
||||
multiuser,
|
||||
|
|
@ -28,14 +13,14 @@ export const NetworkTab = ({
|
|||
remotetunnel,
|
||||
nocertify,
|
||||
websearch,
|
||||
onPortChange,
|
||||
onHostChange,
|
||||
onMultiuserChange,
|
||||
onMultiplayerChange,
|
||||
onRemotetunnelChange,
|
||||
onNocertifyChange,
|
||||
onWebsearchChange,
|
||||
}: NetworkTabProps) => {
|
||||
handlePortChange,
|
||||
handleHostChange,
|
||||
handleMultiuserChange,
|
||||
handleMultiplayerChange,
|
||||
handleRemotetunnelChange,
|
||||
handleNocertifyChange,
|
||||
handleWebsearchChange,
|
||||
} = useLaunchConfig();
|
||||
const [portInput, setPortInput] = useState(port?.toString() ?? '');
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -53,10 +38,10 @@ export const NetworkTab = ({
|
|||
<InfoTooltip label="The hostname or IP address on which KoboldCpp will bind its webserver to." />
|
||||
</Group>
|
||||
<TextInput
|
||||
placeholder="localhost"
|
||||
label="Host"
|
||||
description="The IP address to bind to"
|
||||
value={host}
|
||||
onChange={(event) => onHostChange(event.currentTarget.value)}
|
||||
style={{ maxWidth: 200 }}
|
||||
onChange={(event) => handleHostChange(event.currentTarget.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -80,13 +65,13 @@ export const NetworkTab = ({
|
|||
|
||||
const numValue = Number(value);
|
||||
if (!isNaN(numValue) && numValue >= 1 && numValue <= 65535) {
|
||||
onPortChange(numValue);
|
||||
handlePortChange(numValue);
|
||||
}
|
||||
}}
|
||||
onBlur={(event) => {
|
||||
const value = event.currentTarget.value;
|
||||
if (value === '') {
|
||||
onPortChange(undefined);
|
||||
handlePortChange(undefined);
|
||||
setPortInput('');
|
||||
}
|
||||
}}
|
||||
|
|
@ -106,7 +91,7 @@ export const NetworkTab = ({
|
|||
<Checkbox
|
||||
checked={multiuser}
|
||||
onChange={(event) =>
|
||||
onMultiuserChange(event.currentTarget.checked)
|
||||
handleMultiuserChange(event.currentTarget.checked)
|
||||
}
|
||||
label="Multiuser Mode"
|
||||
/>
|
||||
|
|
@ -119,7 +104,7 @@ export const NetworkTab = ({
|
|||
<Checkbox
|
||||
checked={multiplayer}
|
||||
onChange={(event) =>
|
||||
onMultiplayerChange(event.currentTarget.checked)
|
||||
handleMultiplayerChange(event.currentTarget.checked)
|
||||
}
|
||||
label="Shared Multiplayer"
|
||||
/>
|
||||
|
|
@ -134,7 +119,7 @@ export const NetworkTab = ({
|
|||
<Checkbox
|
||||
checked={remotetunnel}
|
||||
onChange={(event) =>
|
||||
onRemotetunnelChange(event.currentTarget.checked)
|
||||
handleRemotetunnelChange(event.currentTarget.checked)
|
||||
}
|
||||
label="Remote Tunnel"
|
||||
/>
|
||||
|
|
@ -147,7 +132,7 @@ export const NetworkTab = ({
|
|||
<Checkbox
|
||||
checked={nocertify}
|
||||
onChange={(event) =>
|
||||
onNocertifyChange(event.currentTarget.checked)
|
||||
handleNocertifyChange(event.currentTarget.checked)
|
||||
}
|
||||
label="No Certify Mode (Insecure)"
|
||||
/>
|
||||
|
|
@ -160,7 +145,7 @@ export const NetworkTab = ({
|
|||
<Checkbox
|
||||
checked={websearch}
|
||||
onChange={(event) =>
|
||||
onWebsearchChange(event.currentTarget.checked)
|
||||
handleWebsearchChange(event.currentTarget.checked)
|
||||
}
|
||||
label="Enable WebSearch"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { Card, Container, Stack, Tabs, Group, Button } from '@mantine/core';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
import { useTrackedConfigHandlers } from '@/hooks/useTrackedConfigHandlers';
|
||||
import { useLaunchLogic } from '@/hooks/useLaunchLogic';
|
||||
import { useWarnings } from '@/hooks/useWarnings';
|
||||
import { GeneralTab } from '@/components/screens/Launch/GeneralTab';
|
||||
import { GeneralTab } from '@/components/screens/Launch/GeneralTab/index';
|
||||
import { AdvancedTab } from '@/components/screens/Launch/AdvancedTab';
|
||||
import { NetworkTab } from '@/components/screens/Launch/NetworkTab';
|
||||
import { ImageGenerationTab } from '@/components/screens/Launch/ImageGenerationTab';
|
||||
|
|
@ -25,7 +24,6 @@ export const LaunchScreen = ({
|
|||
const [selectedFile, setSelectedFile] = useState<string | null>(null);
|
||||
const [, setInstallDir] = useState<string>('');
|
||||
const [activeTab, setActiveTab] = useState<string | null>('general');
|
||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
||||
const [warnings, setWarnings] = useState<
|
||||
Array<{ type: 'warning' | 'info'; message: string }>
|
||||
>([]);
|
||||
|
|
@ -51,6 +49,7 @@ export const LaunchScreen = ({
|
|||
quantmatmul,
|
||||
backend,
|
||||
gpuDevice,
|
||||
gpuPlatform,
|
||||
sdmodel,
|
||||
sdt5xxl,
|
||||
sdclipl,
|
||||
|
|
@ -59,78 +58,9 @@ export const LaunchScreen = ({
|
|||
sdvae,
|
||||
sdlora,
|
||||
parseAndApplyConfigFile,
|
||||
loadSavedSettings,
|
||||
loadConfigFromFile,
|
||||
handleSelectModelFile,
|
||||
handleGpuDeviceChange,
|
||||
handleSelectSdmodelFile,
|
||||
handleSelectSdt5xxlFile,
|
||||
handleSelectSdcliplFile,
|
||||
handleSelectSdclipgFile,
|
||||
handleSelectSdphotomakerFile,
|
||||
handleSelectSdvaeFile,
|
||||
handleSelectSdloraFile,
|
||||
handleApplyPreset,
|
||||
handleModelPathChange,
|
||||
handleGpuLayersChange,
|
||||
handleAutoGpuLayersChange,
|
||||
handleContextSizeChangeWithStep,
|
||||
handleAdditionalArgumentsChange,
|
||||
handlePortChange,
|
||||
handleHostChange,
|
||||
handleMultiuserChange,
|
||||
handleMultiplayerChange,
|
||||
handleRemotetunnelChange,
|
||||
handleNocertifyChange,
|
||||
handleWebsearchChange,
|
||||
handleNoshiftChange,
|
||||
handleFlashattentionChange,
|
||||
handleNoavx2Change,
|
||||
handleFailsafeChange,
|
||||
handleLowvramChange,
|
||||
handleQuantmatmulChange,
|
||||
handleBackendChange,
|
||||
handleSdmodelChange,
|
||||
handleSdt5xxlChange,
|
||||
handleSdcliplChange,
|
||||
handleSdclipgChange,
|
||||
handleSdphotomakerChange,
|
||||
handleSdvaeChange,
|
||||
handleSdloraChange,
|
||||
} = useLaunchConfig();
|
||||
|
||||
const trackedHandlers = useTrackedConfigHandlers({
|
||||
setHasUnsavedChanges,
|
||||
handlers: {
|
||||
handleModelPathChange,
|
||||
handleGpuLayersChange,
|
||||
handleAutoGpuLayersChange,
|
||||
handleContextSizeChangeWithStep,
|
||||
handleAdditionalArgumentsChange,
|
||||
handlePortChange,
|
||||
handleHostChange,
|
||||
handleMultiuserChange,
|
||||
handleMultiplayerChange,
|
||||
handleRemotetunnelChange,
|
||||
handleNocertifyChange,
|
||||
handleWebsearchChange,
|
||||
handleNoshiftChange,
|
||||
handleFlashattentionChange,
|
||||
handleNoavx2Change,
|
||||
handleFailsafeChange,
|
||||
handleLowvramChange,
|
||||
handleQuantmatmulChange,
|
||||
handleBackendChange,
|
||||
handleSdmodelChange,
|
||||
handleSdt5xxlChange,
|
||||
handleSdcliplChange,
|
||||
handleSdclipgChange,
|
||||
handleSdphotomakerChange,
|
||||
handleSdvaeChange,
|
||||
handleSdloraChange,
|
||||
},
|
||||
});
|
||||
|
||||
const { isLaunching, handleLaunch } = useLaunchLogic({
|
||||
modelPath,
|
||||
sdmodel,
|
||||
|
|
@ -156,13 +86,11 @@ export const LaunchScreen = ({
|
|||
setSelectedFile(files[0].name);
|
||||
}
|
||||
|
||||
await loadSavedSettings();
|
||||
|
||||
const currentSelectedFile = await loadConfigFromFile(files, savedConfig);
|
||||
if (currentSelectedFile && !selectedFile) {
|
||||
setSelectedFile(currentSelectedFile);
|
||||
const loadedConfigFileName = await loadConfigFromFile(files, savedConfig);
|
||||
if (loadedConfigFileName && !selectedFile) {
|
||||
setSelectedFile(loadedConfigFileName);
|
||||
}
|
||||
}, [selectedFile, loadSavedSettings, loadConfigFromFile]);
|
||||
}, [selectedFile, loadConfigFromFile]);
|
||||
|
||||
const handleFileSelection = async (fileName: string) => {
|
||||
setSelectedFile(fileName);
|
||||
|
|
@ -172,14 +100,19 @@ export const LaunchScreen = ({
|
|||
if (selectedConfig) {
|
||||
await parseAndApplyConfigFile(selectedConfig.path);
|
||||
}
|
||||
|
||||
setHasUnsavedChanges(false);
|
||||
};
|
||||
|
||||
const buildConfigData = () => ({
|
||||
const buildConfigData = () => {
|
||||
let useclblastValue: [number, number] | false = false;
|
||||
|
||||
if (backend === 'clblast') {
|
||||
useclblastValue = [gpuDevice, gpuPlatform];
|
||||
}
|
||||
|
||||
return {
|
||||
gpulayers: gpuLayers,
|
||||
contextsize: contextSize,
|
||||
model_param: modelPath,
|
||||
model: modelPath,
|
||||
port,
|
||||
host,
|
||||
multiuser: multiuser ? 1 : 0,
|
||||
|
|
@ -193,14 +126,15 @@ export const LaunchScreen = ({
|
|||
failsafe,
|
||||
usecuda: backend === 'cuda' || backend === 'rocm',
|
||||
usevulkan: backend === 'vulkan',
|
||||
useclblast: backend === 'clblast',
|
||||
useclblast: useclblastValue,
|
||||
sdmodel,
|
||||
sdt5xxl,
|
||||
sdclipl,
|
||||
sdclipg,
|
||||
sdphotomaker,
|
||||
sdvae,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const handleCreateNewConfig = async (configName: string) => {
|
||||
try {
|
||||
|
|
@ -214,7 +148,6 @@ export const LaunchScreen = ({
|
|||
const newFileName = `${configName}.kcpps`;
|
||||
setSelectedFile(newFileName);
|
||||
await window.electronAPI.kobold.setSelectedConfig(newFileName);
|
||||
setHasUnsavedChanges(false);
|
||||
} else {
|
||||
window.electronAPI.logs.logError(
|
||||
'Failed to create new configuration',
|
||||
|
|
@ -246,9 +179,7 @@ export const LaunchScreen = ({
|
|||
buildConfigData()
|
||||
);
|
||||
|
||||
if (success) {
|
||||
setHasUnsavedChanges(false);
|
||||
} else {
|
||||
if (!success) {
|
||||
window.electronAPI.logs.logError(
|
||||
'Failed to save configuration',
|
||||
new Error('Save operation failed')
|
||||
|
|
@ -318,7 +249,6 @@ export const LaunchScreen = ({
|
|||
<ConfigFileManager
|
||||
configFiles={configFiles}
|
||||
selectedFile={selectedFile}
|
||||
hasUnsavedChanges={hasUnsavedChanges}
|
||||
onFileSelection={handleFileSelection}
|
||||
onCreateNewConfig={handleCreateNewConfig}
|
||||
onSaveConfig={handleSaveConfig}
|
||||
|
|
@ -335,138 +265,19 @@ export const LaunchScreen = ({
|
|||
|
||||
<div>
|
||||
<Tabs.Panel value="general" pt="md">
|
||||
<GeneralTab
|
||||
modelPath={modelPath}
|
||||
gpuLayers={gpuLayers}
|
||||
autoGpuLayers={autoGpuLayers}
|
||||
contextSize={contextSize}
|
||||
backend={backend}
|
||||
gpuDevice={gpuDevice}
|
||||
noavx2={noavx2}
|
||||
failsafe={failsafe}
|
||||
onModelPathChange={
|
||||
trackedHandlers.handleModelPathChangeWithTracking
|
||||
}
|
||||
onSelectModelFile={handleSelectModelFile}
|
||||
onGpuLayersChange={
|
||||
trackedHandlers.handleGpuLayersChangeWithTracking
|
||||
}
|
||||
onAutoGpuLayersChange={
|
||||
trackedHandlers.handleAutoGpuLayersChangeWithTracking
|
||||
}
|
||||
onContextSizeChange={
|
||||
trackedHandlers.handleContextSizeChangeWithTracking
|
||||
}
|
||||
onBackendChange={
|
||||
trackedHandlers.handleBackendChangeWithTracking
|
||||
}
|
||||
onGpuDeviceChange={handleGpuDeviceChange}
|
||||
onWarningsChange={setWarnings}
|
||||
/>
|
||||
<GeneralTab onWarningsChange={setWarnings} />
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="advanced" pt="md">
|
||||
<AdvancedTab
|
||||
additionalArguments={additionalArguments}
|
||||
noshift={noshift}
|
||||
flashattention={flashattention}
|
||||
noavx2={noavx2}
|
||||
failsafe={failsafe}
|
||||
lowvram={lowvram}
|
||||
quantmatmul={quantmatmul}
|
||||
backend={backend}
|
||||
onAdditionalArgumentsChange={
|
||||
trackedHandlers.handleAdditionalArgumentsChangeWithTracking
|
||||
}
|
||||
onNoshiftChange={
|
||||
trackedHandlers.handleNoshiftChangeWithTracking
|
||||
}
|
||||
onFlashattentionChange={
|
||||
trackedHandlers.handleFlashattentionChangeWithTracking
|
||||
}
|
||||
onNoavx2Change={
|
||||
trackedHandlers.handleNoavx2ChangeWithTracking
|
||||
}
|
||||
onFailsafeChange={
|
||||
trackedHandlers.handleFailsafeChangeWithTracking
|
||||
}
|
||||
onLowvramChange={
|
||||
trackedHandlers.handleLowvramChangeWithTracking
|
||||
}
|
||||
onQuantmatmulChange={
|
||||
trackedHandlers.handleQuantmatmulChangeWithTracking
|
||||
}
|
||||
/>
|
||||
<AdvancedTab />
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="network" pt="md">
|
||||
<NetworkTab
|
||||
port={port}
|
||||
host={host}
|
||||
multiuser={multiuser}
|
||||
multiplayer={multiplayer}
|
||||
remotetunnel={remotetunnel}
|
||||
nocertify={nocertify}
|
||||
websearch={websearch}
|
||||
onPortChange={trackedHandlers.handlePortChangeWithTracking}
|
||||
onHostChange={trackedHandlers.handleHostChangeWithTracking}
|
||||
onMultiuserChange={
|
||||
trackedHandlers.handleMultiuserChangeWithTracking
|
||||
}
|
||||
onMultiplayerChange={
|
||||
trackedHandlers.handleMultiplayerChangeWithTracking
|
||||
}
|
||||
onRemotetunnelChange={
|
||||
trackedHandlers.handleRemotetunnelChangeWithTracking
|
||||
}
|
||||
onNocertifyChange={
|
||||
trackedHandlers.handleNocertifyChangeWithTracking
|
||||
}
|
||||
onWebsearchChange={
|
||||
trackedHandlers.handleWebsearchChangeWithTracking
|
||||
}
|
||||
/>
|
||||
<NetworkTab />
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="image" pt="md">
|
||||
<ImageGenerationTab
|
||||
sdmodel={sdmodel}
|
||||
sdt5xxl={sdt5xxl}
|
||||
sdclipl={sdclipl}
|
||||
sdclipg={sdclipg}
|
||||
sdphotomaker={sdphotomaker}
|
||||
sdvae={sdvae}
|
||||
sdlora={sdlora}
|
||||
onSdmodelChange={
|
||||
trackedHandlers.handleSdmodelChangeWithTracking
|
||||
}
|
||||
onSelectSdmodelFile={handleSelectSdmodelFile}
|
||||
onSdt5xxlChange={
|
||||
trackedHandlers.handleSdt5xxlChangeWithTracking
|
||||
}
|
||||
onSelectSdt5xxlFile={handleSelectSdt5xxlFile}
|
||||
onSdcliplChange={
|
||||
trackedHandlers.handleSdcliplChangeWithTracking
|
||||
}
|
||||
onSelectSdcliplFile={handleSelectSdcliplFile}
|
||||
onSdclipgChange={
|
||||
trackedHandlers.handleSdclipgChangeWithTracking
|
||||
}
|
||||
onSelectSdclipgFile={handleSelectSdclipgFile}
|
||||
onSdphotomakerChange={
|
||||
trackedHandlers.handleSdphotomakerChangeWithTracking
|
||||
}
|
||||
onSelectSdphotomakerFile={handleSelectSdphotomakerFile}
|
||||
onSdvaeChange={
|
||||
trackedHandlers.handleSdvaeChangeWithTracking
|
||||
}
|
||||
onSelectSdvaeFile={handleSelectSdvaeFile}
|
||||
onSdloraChange={
|
||||
trackedHandlers.handleSdloraChangeWithTracking
|
||||
}
|
||||
onSelectSdloraFile={handleSelectSdloraFile}
|
||||
onApplyPreset={handleApplyPreset}
|
||||
/>
|
||||
<ImageGenerationTab />
|
||||
</Tabs.Panel>
|
||||
</div>
|
||||
</Tabs>
|
||||
|
|
|
|||
0
src/hooks/useChangeTracking.ts
Normal file
0
src/hooks/useChangeTracking.ts
Normal file
|
|
@ -1,534 +1,90 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import type { ConfigFile } from '@/types';
|
||||
import { useLaunchConfigStore } from '@/stores/launchConfigStore';
|
||||
import {
|
||||
getPresetByName,
|
||||
IMAGE_MODEL_PRESETS,
|
||||
type ImageModelPreset,
|
||||
} from '@/utils/imageModelPresets';
|
||||
import {
|
||||
DEFAULT_CONTEXT_SIZE,
|
||||
DEFAULT_MODEL_URL,
|
||||
DEFAULT_HOST,
|
||||
} from '@/constants';
|
||||
|
||||
export const useLaunchConfig = () => {
|
||||
const [gpuLayers, setGpuLayers] = useState<number>(0);
|
||||
const [autoGpuLayers, setAutoGpuLayers] = useState<boolean>(false);
|
||||
const [contextSize, setContextSize] = useState<number>(DEFAULT_CONTEXT_SIZE);
|
||||
const [modelPath, setModelPath] = useState<string>(DEFAULT_MODEL_URL);
|
||||
const [additionalArguments, setAdditionalArguments] = useState<string>('');
|
||||
const [port, setPort] = useState<number | undefined>(undefined);
|
||||
const [host, setHost] = useState<string>(DEFAULT_HOST);
|
||||
const [multiuser, setMultiuser] = useState<boolean>(false);
|
||||
const [multiplayer, setMultiplayer] = useState<boolean>(false);
|
||||
const [remotetunnel, setRemotetunnel] = useState<boolean>(false);
|
||||
const [nocertify, setNocertify] = useState<boolean>(false);
|
||||
const [websearch, setWebsearch] = useState<boolean>(false);
|
||||
const [noshift, setNoshift] = useState<boolean>(false);
|
||||
const [flashattention, setFlashattention] = useState<boolean>(true);
|
||||
const [noavx2, setNoavx2] = useState<boolean>(false);
|
||||
const [failsafe, setFailsafe] = useState<boolean>(false);
|
||||
const [lowvram, setLowvram] = useState<boolean>(false);
|
||||
const [quantmatmul, setQuantmatmul] = useState<boolean>(true);
|
||||
const [backend, setBackend] = useState<string>('');
|
||||
const [gpuDevice, setGpuDevice] = useState<number>(0);
|
||||
|
||||
const [sdmodel, setSdmodel] = useState<string>('');
|
||||
const [sdt5xxl, setSdt5xxl] = useState<string>('');
|
||||
const [sdclipl, setSdclipl] = useState<string>('');
|
||||
const [sdclipg, setSdclipg] = useState<string>('');
|
||||
const [sdphotomaker, setSdphotomaker] = useState<string>('');
|
||||
const [sdvae, setSdvae] = useState<string>('');
|
||||
const [sdlora, setSdlora] = useState<string>('');
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
const parseAndApplyConfigFile = useCallback(async (configPath: string) => {
|
||||
const configData =
|
||||
await window.electronAPI.kobold.parseConfigFile(configPath);
|
||||
|
||||
if (configData) {
|
||||
if (typeof configData.gpulayers === 'number') {
|
||||
setGpuLayers(configData.gpulayers);
|
||||
} else {
|
||||
setGpuLayers(0);
|
||||
}
|
||||
|
||||
if (typeof configData.contextsize === 'number') {
|
||||
setContextSize(configData.contextsize);
|
||||
} else {
|
||||
setContextSize(DEFAULT_CONTEXT_SIZE);
|
||||
}
|
||||
|
||||
if (typeof configData.model_param === 'string') {
|
||||
setModelPath(configData.model_param);
|
||||
}
|
||||
|
||||
if (typeof configData.port === 'number') {
|
||||
setPort(configData.port);
|
||||
} else {
|
||||
setPort(undefined);
|
||||
}
|
||||
|
||||
if (typeof configData.host === 'string') {
|
||||
setHost(configData.host);
|
||||
} else {
|
||||
setHost(DEFAULT_HOST);
|
||||
}
|
||||
|
||||
if (typeof configData.multiuser === 'number') {
|
||||
setMultiuser(configData.multiuser === 1);
|
||||
} else {
|
||||
setMultiuser(false);
|
||||
}
|
||||
|
||||
if (typeof configData.multiplayer === 'boolean') {
|
||||
setMultiplayer(configData.multiplayer);
|
||||
} else {
|
||||
setMultiplayer(false);
|
||||
}
|
||||
|
||||
if (typeof configData.remotetunnel === 'boolean') {
|
||||
setRemotetunnel(configData.remotetunnel);
|
||||
} else {
|
||||
setRemotetunnel(false);
|
||||
}
|
||||
|
||||
if (typeof configData.nocertify === 'boolean') {
|
||||
setNocertify(configData.nocertify);
|
||||
} else {
|
||||
setNocertify(false);
|
||||
}
|
||||
|
||||
if (typeof configData.websearch === 'boolean') {
|
||||
setWebsearch(configData.websearch);
|
||||
} else {
|
||||
setWebsearch(false);
|
||||
}
|
||||
|
||||
if (typeof configData.noshift === 'boolean') {
|
||||
setNoshift(configData.noshift);
|
||||
} else {
|
||||
setNoshift(false);
|
||||
}
|
||||
|
||||
if (typeof configData.flashattention === 'boolean') {
|
||||
setFlashattention(configData.flashattention);
|
||||
} else {
|
||||
setFlashattention(true);
|
||||
}
|
||||
|
||||
if (typeof configData.noavx2 === 'boolean') {
|
||||
setNoavx2(configData.noavx2);
|
||||
} else {
|
||||
setNoavx2(false);
|
||||
}
|
||||
|
||||
if (typeof configData.failsafe === 'boolean') {
|
||||
setFailsafe(configData.failsafe);
|
||||
} else {
|
||||
setFailsafe(false);
|
||||
}
|
||||
|
||||
if (typeof configData.lowvram === 'boolean') {
|
||||
setLowvram(configData.lowvram);
|
||||
}
|
||||
|
||||
if (typeof configData.quantmatmul === 'boolean') {
|
||||
setQuantmatmul(configData.quantmatmul);
|
||||
}
|
||||
|
||||
if (configData.usecuda === true) {
|
||||
const gpuInfo = await window.electronAPI.kobold.detectGPU();
|
||||
setBackend(gpuInfo.hasNVIDIA ? 'cuda' : 'rocm');
|
||||
|
||||
if (
|
||||
Array.isArray(configData.usecuda) &&
|
||||
configData.usecuda.length >= 3
|
||||
) {
|
||||
const [vramMode, deviceId, mmqMode] = configData.usecuda;
|
||||
setLowvram(vramMode === 'lowvram');
|
||||
setGpuDevice(parseInt(deviceId, 10) || 0);
|
||||
setQuantmatmul(mmqMode === 'mmq');
|
||||
}
|
||||
} else if (configData.usevulkan === true) {
|
||||
setBackend('vulkan');
|
||||
} else if (configData.useclblast === true) {
|
||||
setBackend('clblast');
|
||||
} else {
|
||||
setBackend('cpu');
|
||||
}
|
||||
|
||||
if (typeof configData.sdmodel === 'string') {
|
||||
setSdmodel(configData.sdmodel);
|
||||
}
|
||||
|
||||
if (typeof configData.sdt5xxl === 'string') {
|
||||
setSdt5xxl(configData.sdt5xxl);
|
||||
}
|
||||
|
||||
if (typeof configData.sdclipl === 'string') {
|
||||
setSdclipl(configData.sdclipl);
|
||||
}
|
||||
|
||||
if (typeof configData.sdclipg === 'string') {
|
||||
setSdclipg(configData.sdclipg);
|
||||
}
|
||||
|
||||
if (typeof configData.sdphotomaker === 'string') {
|
||||
setSdphotomaker(configData.sdphotomaker);
|
||||
}
|
||||
|
||||
if (typeof configData.sdvae === 'string') {
|
||||
setSdvae(configData.sdvae);
|
||||
}
|
||||
|
||||
if (typeof configData.sdlora === 'string') {
|
||||
setSdlora(configData.sdlora);
|
||||
}
|
||||
} else {
|
||||
const cpuCapabilities = await window.electronAPI.kobold.detectCPU();
|
||||
setGpuLayers(0);
|
||||
setContextSize(DEFAULT_CONTEXT_SIZE);
|
||||
setPort(undefined);
|
||||
setHost(DEFAULT_HOST);
|
||||
setMultiuser(false);
|
||||
setMultiplayer(false);
|
||||
setRemotetunnel(false);
|
||||
setNocertify(false);
|
||||
setWebsearch(false);
|
||||
setNoshift(false);
|
||||
setFlashattention(true);
|
||||
setNoavx2(!cpuCapabilities.avx2);
|
||||
setFailsafe(!cpuCapabilities.avx && !cpuCapabilities.avx2);
|
||||
setBackend('');
|
||||
|
||||
setSdmodel('');
|
||||
setSdt5xxl(
|
||||
'https://huggingface.co/camenduru/FLUX.1-dev/resolve/main/t5xxl_fp8_e4m3fn.safetensors?download=true'
|
||||
);
|
||||
setSdclipl(
|
||||
'https://huggingface.co/camenduru/FLUX.1-dev/resolve/main/clip_l.safetensors?download=true'
|
||||
);
|
||||
setSdclipg('');
|
||||
setSdphotomaker('');
|
||||
setSdvae(
|
||||
'https://huggingface.co/camenduru/FLUX.1-dev/resolve/main/ae.safetensors?download=true'
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const loadSavedSettings = useCallback(async () => {
|
||||
const cpuCapabilities = await window.electronAPI.kobold.detectCPU();
|
||||
|
||||
setModelPath(DEFAULT_MODEL_URL);
|
||||
setGpuLayers(0);
|
||||
setContextSize(DEFAULT_CONTEXT_SIZE);
|
||||
setPort(undefined);
|
||||
setHost(DEFAULT_HOST);
|
||||
setMultiuser(false);
|
||||
setMultiplayer(false);
|
||||
setRemotetunnel(false);
|
||||
setNocertify(false);
|
||||
setWebsearch(false);
|
||||
setNoshift(false);
|
||||
setFlashattention(true);
|
||||
setNoavx2(!cpuCapabilities.avx2);
|
||||
setFailsafe(!cpuCapabilities.avx && !cpuCapabilities.avx2);
|
||||
setBackend('');
|
||||
|
||||
setSdmodel('');
|
||||
setSdt5xxl('');
|
||||
setSdclipl('');
|
||||
setSdclipg('');
|
||||
setSdphotomaker('');
|
||||
setSdvae('');
|
||||
}, []);
|
||||
|
||||
const loadConfigFromFile = useCallback(
|
||||
async (configFiles: ConfigFile[], savedConfig: string | null) => {
|
||||
let currentSelectedFile = null;
|
||||
|
||||
if (savedConfig && configFiles.some((f) => f.name === savedConfig)) {
|
||||
currentSelectedFile = savedConfig;
|
||||
} else if (configFiles.length > 0) {
|
||||
currentSelectedFile = configFiles[0].name;
|
||||
}
|
||||
|
||||
if (currentSelectedFile) {
|
||||
const selectedConfig = configFiles.find(
|
||||
(f) => f.name === currentSelectedFile
|
||||
);
|
||||
if (selectedConfig) {
|
||||
await parseAndApplyConfigFile(selectedConfig.path);
|
||||
}
|
||||
}
|
||||
|
||||
return currentSelectedFile;
|
||||
},
|
||||
[parseAndApplyConfigFile]
|
||||
);
|
||||
|
||||
const handleGpuLayersChange = useCallback(async (value: number) => {
|
||||
setGpuLayers(value);
|
||||
}, []);
|
||||
|
||||
const roundToValidContextSize = useCallback((value: number): number => {
|
||||
if (value < 1024) {
|
||||
return Math.round(value / 256) * 256;
|
||||
}
|
||||
return Math.round(value / 1024) * 1024;
|
||||
}, []);
|
||||
|
||||
const handleContextSizeChangeWithStep = useCallback(
|
||||
async (value: number) => {
|
||||
const roundedValue = roundToValidContextSize(value);
|
||||
setContextSize(roundedValue);
|
||||
},
|
||||
[roundToValidContextSize]
|
||||
);
|
||||
|
||||
const handleModelPathChange = useCallback((value: string) => {
|
||||
setModelPath(value);
|
||||
}, []);
|
||||
|
||||
const handleSelectModelFile = useCallback(async () => {
|
||||
const filePath = await window.electronAPI.kobold.selectModelFile();
|
||||
if (filePath) {
|
||||
handleModelPathChange(filePath);
|
||||
}
|
||||
}, [handleModelPathChange]);
|
||||
|
||||
const handleAdditionalArgumentsChange = useCallback((value: string) => {
|
||||
setAdditionalArguments(value);
|
||||
}, []);
|
||||
|
||||
const handleAutoGpuLayersChange = useCallback((checked: boolean) => {
|
||||
setAutoGpuLayers(checked);
|
||||
}, []);
|
||||
|
||||
const handlePortChange = useCallback((value: number | undefined) => {
|
||||
setPort(value);
|
||||
}, []);
|
||||
|
||||
const handleHostChange = useCallback((value: string) => {
|
||||
setHost(value);
|
||||
}, []);
|
||||
|
||||
const handleMultiuserChange = useCallback((checked: boolean) => {
|
||||
setMultiuser(checked);
|
||||
}, []);
|
||||
|
||||
const handleMultiplayerChange = useCallback((checked: boolean) => {
|
||||
setMultiplayer(checked);
|
||||
}, []);
|
||||
|
||||
const handleRemotetunnelChange = useCallback((checked: boolean) => {
|
||||
setRemotetunnel(checked);
|
||||
}, []);
|
||||
|
||||
const handleNocertifyChange = useCallback((checked: boolean) => {
|
||||
setNocertify(checked);
|
||||
}, []);
|
||||
|
||||
const handleWebsearchChange = useCallback((checked: boolean) => {
|
||||
setWebsearch(checked);
|
||||
}, []);
|
||||
|
||||
const handleNoshiftChange = useCallback((checked: boolean) => {
|
||||
setNoshift(checked);
|
||||
}, []);
|
||||
|
||||
const handleFlashattentionChange = useCallback((checked: boolean) => {
|
||||
setFlashattention(checked);
|
||||
}, []);
|
||||
|
||||
const handleNoavx2Change = useCallback((checked: boolean) => {
|
||||
setNoavx2(checked);
|
||||
}, []);
|
||||
|
||||
const handleFailsafeChange = useCallback((checked: boolean) => {
|
||||
setFailsafe(checked);
|
||||
}, []);
|
||||
|
||||
const handleLowvramChange = useCallback((checked: boolean) => {
|
||||
setLowvram(checked);
|
||||
}, []);
|
||||
|
||||
const handleQuantmatmulChange = useCallback((checked: boolean) => {
|
||||
setQuantmatmul(checked);
|
||||
}, []);
|
||||
|
||||
const handleBackendChange = useCallback((backend: string) => {
|
||||
setBackend(backend);
|
||||
}, []);
|
||||
|
||||
const handleGpuDeviceChange = useCallback((device: number) => {
|
||||
setGpuDevice(device);
|
||||
}, []);
|
||||
|
||||
const handleSdmodelChange = useCallback((path: string) => {
|
||||
setSdmodel(path);
|
||||
}, []);
|
||||
|
||||
const handleSelectSdmodelFile = useCallback(async () => {
|
||||
const filePath = await window.electronAPI.kobold.selectModelFile();
|
||||
if (filePath) {
|
||||
setSdmodel(filePath);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSdt5xxlChange = useCallback((path: string) => {
|
||||
setSdt5xxl(path);
|
||||
}, []);
|
||||
|
||||
const handleSelectSdt5xxlFile = useCallback(async () => {
|
||||
const filePath = await window.electronAPI.kobold.selectModelFile();
|
||||
if (filePath) {
|
||||
setSdt5xxl(filePath);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSdcliplChange = useCallback((path: string) => {
|
||||
setSdclipl(path);
|
||||
}, []);
|
||||
|
||||
const handleSelectSdcliplFile = useCallback(async () => {
|
||||
const filePath = await window.electronAPI.kobold.selectModelFile();
|
||||
if (filePath) {
|
||||
setSdclipl(filePath);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSdclipgChange = useCallback((path: string) => {
|
||||
setSdclipg(path);
|
||||
}, []);
|
||||
|
||||
const handleSelectSdclipgFile = useCallback(async () => {
|
||||
const filePath = await window.electronAPI.kobold.selectModelFile();
|
||||
if (filePath) {
|
||||
setSdclipg(filePath);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSdphotomakerChange = useCallback((path: string) => {
|
||||
setSdphotomaker(path);
|
||||
}, []);
|
||||
|
||||
const handleSelectSdphotomakerFile = useCallback(async () => {
|
||||
const filePath = await window.electronAPI.kobold.selectModelFile();
|
||||
if (filePath) {
|
||||
setSdphotomaker(filePath);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSdvaeChange = useCallback((path: string) => {
|
||||
setSdvae(path);
|
||||
}, []);
|
||||
|
||||
const handleSelectSdvaeFile = useCallback(async () => {
|
||||
const filePath = await window.electronAPI.kobold.selectModelFile();
|
||||
if (filePath) {
|
||||
setSdvae(filePath);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSdloraChange = useCallback((path: string) => {
|
||||
setSdlora(path);
|
||||
}, []);
|
||||
|
||||
const handleSelectSdloraFile = useCallback(async () => {
|
||||
const filePath = await window.electronAPI.kobold.selectModelFile();
|
||||
if (filePath) {
|
||||
setSdlora(filePath);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const applyImageModelPreset = useCallback((preset: ImageModelPreset) => {
|
||||
setSdmodel(preset.sdmodel);
|
||||
setSdt5xxl(preset.sdt5xxl);
|
||||
setSdclipl(preset.sdclipl);
|
||||
setSdclipg(preset.sdclipg);
|
||||
setSdphotomaker(preset.sdphotomaker);
|
||||
setSdvae(preset.sdvae);
|
||||
}, []);
|
||||
|
||||
const handleApplyPreset = useCallback(
|
||||
(presetName: string) => {
|
||||
const preset = getPresetByName(presetName);
|
||||
if (preset) {
|
||||
applyImageModelPreset(preset);
|
||||
}
|
||||
},
|
||||
[applyImageModelPreset]
|
||||
);
|
||||
const state = useLaunchConfigStore();
|
||||
|
||||
return {
|
||||
gpuLayers,
|
||||
autoGpuLayers,
|
||||
contextSize,
|
||||
modelPath,
|
||||
additionalArguments,
|
||||
port,
|
||||
host,
|
||||
multiuser,
|
||||
multiplayer,
|
||||
remotetunnel,
|
||||
nocertify,
|
||||
websearch,
|
||||
noshift,
|
||||
flashattention,
|
||||
noavx2,
|
||||
failsafe,
|
||||
lowvram,
|
||||
quantmatmul,
|
||||
backend,
|
||||
gpuDevice,
|
||||
sdmodel,
|
||||
sdt5xxl,
|
||||
sdclipl,
|
||||
sdclipg,
|
||||
sdphotomaker,
|
||||
sdvae,
|
||||
sdlora,
|
||||
gpuLayers: state.gpuLayers,
|
||||
autoGpuLayers: state.autoGpuLayers,
|
||||
contextSize: state.contextSize,
|
||||
modelPath: state.modelPath,
|
||||
additionalArguments: state.additionalArguments,
|
||||
port: state.port,
|
||||
host: state.host,
|
||||
multiuser: state.multiuser,
|
||||
multiplayer: state.multiplayer,
|
||||
remotetunnel: state.remotetunnel,
|
||||
nocertify: state.nocertify,
|
||||
websearch: state.websearch,
|
||||
noshift: state.noshift,
|
||||
flashattention: state.flashattention,
|
||||
noavx2: state.noavx2,
|
||||
failsafe: state.failsafe,
|
||||
lowvram: state.lowvram,
|
||||
quantmatmul: state.quantmatmul,
|
||||
backend: state.backend,
|
||||
gpuDevice: state.gpuDevice,
|
||||
gpuPlatform: state.gpuPlatform,
|
||||
sdmodel: state.sdmodel,
|
||||
sdt5xxl: state.sdt5xxl,
|
||||
sdclipl: state.sdclipl,
|
||||
sdclipg: state.sdclipg,
|
||||
sdphotomaker: state.sdphotomaker,
|
||||
sdvae: state.sdvae,
|
||||
sdlora: state.sdlora,
|
||||
|
||||
parseAndApplyConfigFile,
|
||||
loadSavedSettings,
|
||||
loadConfigFromFile,
|
||||
handleGpuLayersChange,
|
||||
handleAutoGpuLayersChange,
|
||||
handleContextSizeChangeWithStep,
|
||||
handleModelPathChange,
|
||||
handleSelectModelFile,
|
||||
handleAdditionalArgumentsChange,
|
||||
handlePortChange,
|
||||
handleHostChange,
|
||||
handleMultiuserChange,
|
||||
handleMultiplayerChange,
|
||||
handleRemotetunnelChange,
|
||||
handleNocertifyChange,
|
||||
handleWebsearchChange,
|
||||
handleNoshiftChange,
|
||||
handleFlashattentionChange,
|
||||
handleNoavx2Change,
|
||||
handleFailsafeChange,
|
||||
handleLowvramChange,
|
||||
handleQuantmatmulChange,
|
||||
handleBackendChange,
|
||||
handleGpuDeviceChange,
|
||||
handleSdmodelChange,
|
||||
handleSelectSdmodelFile,
|
||||
handleSdt5xxlChange,
|
||||
handleSelectSdt5xxlFile,
|
||||
handleSdcliplChange,
|
||||
handleSelectSdcliplFile,
|
||||
handleSdclipgChange,
|
||||
handleSelectSdclipgFile,
|
||||
handleSdphotomakerChange,
|
||||
handleSelectSdphotomakerFile,
|
||||
handleSdvaeChange,
|
||||
handleSelectSdvaeFile,
|
||||
handleSdloraChange,
|
||||
handleSelectSdloraFile,
|
||||
applyImageModelPreset,
|
||||
handleApplyPreset,
|
||||
handleGpuLayersChange: state.setGpuLayers,
|
||||
handleAutoGpuLayersChange: state.setAutoGpuLayers,
|
||||
handleContextSizeChangeWithStep: state.contextSizeChangeWithStep,
|
||||
handleModelPathChange: state.setModelPath,
|
||||
handleAdditionalArgumentsChange: state.setAdditionalArguments,
|
||||
handlePortChange: state.setPort,
|
||||
handleHostChange: state.setHost,
|
||||
handleMultiuserChange: state.setMultiuser,
|
||||
handleMultiplayerChange: state.setMultiplayer,
|
||||
handleRemotetunnelChange: state.setRemotetunnel,
|
||||
handleNocertifyChange: state.setNocertify,
|
||||
handleWebsearchChange: state.setWebsearch,
|
||||
handleNoshiftChange: state.setNoshift,
|
||||
handleFlashattentionChange: state.setFlashattention,
|
||||
handleNoavx2Change: state.setNoavx2,
|
||||
handleFailsafeChange: state.setFailsafe,
|
||||
handleLowvramChange: state.setLowvram,
|
||||
handleQuantmatmulChange: state.setQuantmatmul,
|
||||
handleBackendChange: state.setBackend,
|
||||
handleGpuDeviceChange: state.setGpuDevice,
|
||||
handleGpuPlatformChange: state.setGpuPlatform,
|
||||
handleSdmodelChange: state.setSdmodel,
|
||||
handleSdt5xxlChange: state.setSdt5xxl,
|
||||
handleSdcliplChange: state.setSdclipl,
|
||||
handleSdclipgChange: state.setSdclipg,
|
||||
handleSdphotomakerChange: state.setSdphotomaker,
|
||||
handleSdvaeChange: state.setSdvae,
|
||||
handleSdloraChange: state.setSdlora,
|
||||
|
||||
parseAndApplyConfigFile: state.parseAndApplyConfigFile,
|
||||
loadConfigFromFile: state.loadConfigFromFile,
|
||||
handleSelectModelFile: state.selectModelFile,
|
||||
handleImageModelPresetChange: state.applyImageModelPreset,
|
||||
handleApplyPreset: (presetName: string) => {
|
||||
const preset = IMAGE_MODEL_PRESETS.find(
|
||||
(p: ImageModelPreset) => p.name === presetName
|
||||
);
|
||||
if (preset) {
|
||||
state.applyImageModelPreset(preset);
|
||||
}
|
||||
},
|
||||
|
||||
handleSelectSdmodelFile: state.selectSdmodelFile,
|
||||
handleSelectSdt5xxlFile: state.selectSdt5xxlFile,
|
||||
handleSelectSdcliplFile: state.selectSdcliplFile,
|
||||
handleSelectSdclipgFile: state.selectSdclipgFile,
|
||||
handleSelectSdphotomakerFile: state.selectSdphotomakerFile,
|
||||
handleSelectSdvaeFile: state.selectSdvaeFile,
|
||||
handleSelectSdloraFile: state.selectSdloraFile,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import { parseCLBlastDevice } from '@/utils';
|
||||
|
||||
interface UseLaunchLogicProps {
|
||||
modelPath: string;
|
||||
|
|
@ -22,7 +23,7 @@ interface LaunchArgs {
|
|||
flashattention: boolean;
|
||||
backend: string;
|
||||
lowvram: boolean;
|
||||
gpuDevice: number;
|
||||
gpuDevice: number | string;
|
||||
quantmatmul: boolean;
|
||||
additionalArguments: string;
|
||||
sdt5xxl: string;
|
||||
|
|
@ -116,13 +117,33 @@ const buildBackendArgs = (launchArgs: LaunchArgs): string[] => {
|
|||
if (launchArgs.backend === 'cuda' || launchArgs.backend === 'rocm') {
|
||||
const cudaArgs = ['--usecuda'];
|
||||
cudaArgs.push(launchArgs.lowvram ? 'lowvram' : 'normal');
|
||||
cudaArgs.push(launchArgs.gpuDevice.toString());
|
||||
cudaArgs.push(
|
||||
typeof launchArgs.gpuDevice === 'string'
|
||||
? '0'
|
||||
: launchArgs.gpuDevice.toString()
|
||||
);
|
||||
cudaArgs.push(launchArgs.quantmatmul ? 'mmq' : 'nommq');
|
||||
args.push(...cudaArgs);
|
||||
} else if (launchArgs.backend === 'vulkan') {
|
||||
args.push('--usevulkan');
|
||||
} else if (launchArgs.backend === 'clblast') {
|
||||
args.push('--useclblast');
|
||||
const clblastArgs = ['--useclblast'];
|
||||
|
||||
if (typeof launchArgs.gpuDevice === 'string') {
|
||||
const parsed = parseCLBlastDevice(launchArgs.gpuDevice);
|
||||
if (parsed) {
|
||||
clblastArgs.push(
|
||||
parsed.deviceIndex.toString(),
|
||||
parsed.platformIndex.toString()
|
||||
);
|
||||
} else {
|
||||
clblastArgs.push('0', '0');
|
||||
}
|
||||
} else {
|
||||
clblastArgs.push(launchArgs.gpuDevice.toString(), '0');
|
||||
}
|
||||
|
||||
args.push(...clblastArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ interface TrackedConfigHandlersProps {
|
|||
handleLowvramChange: (enabled: boolean) => void;
|
||||
handleQuantmatmulChange: (enabled: boolean) => void;
|
||||
handleBackendChange: (backend: string) => void;
|
||||
handleGpuDeviceChange: (device: number) => void;
|
||||
handleGpuPlatformChange: (platform: number) => void;
|
||||
handleSdmodelChange: (path: string) => void;
|
||||
handleSdt5xxlChange: (path: string) => void;
|
||||
handleSdcliplChange: (path: string) => void;
|
||||
|
|
@ -58,6 +60,8 @@ export const useTrackedConfigHandlers = ({
|
|||
handleLowvramChange,
|
||||
handleQuantmatmulChange,
|
||||
handleBackendChange,
|
||||
handleGpuDeviceChange,
|
||||
handleGpuPlatformChange,
|
||||
handleSdmodelChange,
|
||||
handleSdt5xxlChange,
|
||||
handleSdcliplChange,
|
||||
|
|
@ -144,6 +148,14 @@ export const useTrackedConfigHandlers = ({
|
|||
handleBackendChange,
|
||||
setHasUnsavedChanges
|
||||
),
|
||||
handleGpuDeviceChangeWithTracking: createChangeTracker(
|
||||
handleGpuDeviceChange,
|
||||
setHasUnsavedChanges
|
||||
),
|
||||
handleGpuPlatformChangeWithTracking: createChangeTracker(
|
||||
handleGpuPlatformChange,
|
||||
setHasUnsavedChanges
|
||||
),
|
||||
handleSdmodelChangeWithTracking: createChangeTracker(
|
||||
handleSdmodelChange,
|
||||
setHasUnsavedChanges
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ export class KoboldCppManager {
|
|||
async parseConfigFile(filePath: string): Promise<{
|
||||
gpulayers?: number;
|
||||
contextsize?: number;
|
||||
model_param?: string;
|
||||
model?: string;
|
||||
[key: string]: unknown;
|
||||
} | null> {
|
||||
try {
|
||||
|
|
@ -371,7 +371,7 @@ export class KoboldCppManager {
|
|||
configData: {
|
||||
gpulayers?: number;
|
||||
contextsize?: number;
|
||||
model_param?: string;
|
||||
model?: string;
|
||||
port?: number;
|
||||
host?: string;
|
||||
multiuser?: number;
|
||||
|
|
@ -385,7 +385,7 @@ export class KoboldCppManager {
|
|||
failsafe?: boolean;
|
||||
usecuda?: boolean;
|
||||
usevulkan?: boolean;
|
||||
useclblast?: boolean;
|
||||
useclblast?: [number, number] | boolean;
|
||||
sdmodel?: string;
|
||||
sdt5xxl?: string;
|
||||
sdclipl?: string;
|
||||
|
|
@ -615,6 +615,7 @@ export class KoboldCppManager {
|
|||
|
||||
this.koboldProcess = spawn(versionPath, args, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
|
||||
if (onOutput) {
|
||||
|
|
@ -938,6 +939,7 @@ export class KoboldCppManager {
|
|||
const child = spawn(currentVersion.path, finalArgs, {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
detached: false,
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
|
||||
this.koboldProcess = child;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ export interface BackendSupport {
|
|||
|
||||
export class BinaryService {
|
||||
private backendSupportCache = new Map<string, BackendSupport>();
|
||||
private availableBackendsCache = new Map<
|
||||
string,
|
||||
Array<{ value: string; label: string; devices?: string[] }>
|
||||
>();
|
||||
private logManager: LogManager;
|
||||
|
||||
constructor(logManager: LogManager) {
|
||||
|
|
@ -78,6 +82,12 @@ export class BinaryService {
|
|||
clblast: { supported: boolean; devices: string[] };
|
||||
}
|
||||
): Array<{ value: string; label: string; devices?: string[] }> {
|
||||
const cacheKey = `${koboldBinaryPath}:${JSON.stringify(hardwareCapabilities)}`;
|
||||
|
||||
if (this.availableBackendsCache.has(cacheKey)) {
|
||||
return this.availableBackendsCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
const backendSupport = this.detectBackendSupport(koboldBinaryPath);
|
||||
const backends: Array<{
|
||||
value: string;
|
||||
|
|
@ -122,10 +132,12 @@ export class BinaryService {
|
|||
label: 'CPU',
|
||||
});
|
||||
|
||||
this.availableBackendsCache.set(cacheKey, backends);
|
||||
return backends;
|
||||
}
|
||||
|
||||
clearCache(): void {
|
||||
this.backendSupportCache.clear();
|
||||
this.availableBackendsCache.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-comments/disallowComments */
|
||||
import si from 'systeminformation';
|
||||
import * as openclInfo from 'opencl-info';
|
||||
import { shortenDeviceName } from '@/utils';
|
||||
import type {
|
||||
CPUCapabilities,
|
||||
|
|
@ -343,74 +344,35 @@ export class HardwareService {
|
|||
devices: string[];
|
||||
}> {
|
||||
try {
|
||||
const { spawn } = await import('child_process');
|
||||
const clinfo = spawn('clinfo', ['--json'], { timeout: 5000 });
|
||||
const platforms = openclInfo.getPlatformInfo();
|
||||
|
||||
let output = '';
|
||||
clinfo.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
clinfo.on('close', (code) => {
|
||||
if (code === 0 && output.trim()) {
|
||||
try {
|
||||
const data = JSON.parse(output);
|
||||
const devices: string[] = [];
|
||||
|
||||
if (data.platforms) {
|
||||
for (const platform of data.platforms) {
|
||||
for (
|
||||
let platformIndex = 0;
|
||||
platformIndex < platforms.length;
|
||||
platformIndex++
|
||||
) {
|
||||
const platform = platforms[platformIndex];
|
||||
if (platform.devices) {
|
||||
for (const device of platform.devices) {
|
||||
if (device.name && device.type !== 'CPU') {
|
||||
devices.push(shortenDeviceName(device.name));
|
||||
}
|
||||
for (
|
||||
let deviceIndex = 0;
|
||||
deviceIndex < platform.devices.length;
|
||||
deviceIndex++
|
||||
) {
|
||||
const device = platform.devices[deviceIndex];
|
||||
if (device.name && device.type === 'GPU') {
|
||||
const deviceLabel = `${shortenDeviceName(device.name)} (${platform.name})`;
|
||||
devices.push(deviceLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve({
|
||||
return {
|
||||
supported: devices.length > 0,
|
||||
devices,
|
||||
});
|
||||
} catch {
|
||||
const lines = output.split('\n');
|
||||
const devices: string[] = [];
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.includes('Device Name') && !line.includes('CPU')) {
|
||||
const name = line.split(':')[1]?.trim();
|
||||
if (name) {
|
||||
devices.push(shortenDeviceName(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve({
|
||||
supported: devices.length > 0,
|
||||
devices,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
resolve({ supported: false, devices: [] });
|
||||
}
|
||||
});
|
||||
|
||||
clinfo.on('error', () => {
|
||||
resolve({ supported: false, devices: [] });
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
clinfo.kill('SIGTERM');
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
resolve({ supported: false, devices: [] });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
} catch {
|
||||
return { supported: false, devices: [] };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ const koboldAPI: KoboldAPI = {
|
|||
configData: {
|
||||
gpulayers?: number;
|
||||
contextsize?: number;
|
||||
model_param?: string;
|
||||
model?: string;
|
||||
port?: number;
|
||||
host?: string;
|
||||
multiuser?: number;
|
||||
|
|
@ -69,7 +69,7 @@ const koboldAPI: KoboldAPI = {
|
|||
failsafe?: boolean;
|
||||
usecuda?: boolean;
|
||||
usevulkan?: boolean;
|
||||
useclblast?: boolean;
|
||||
useclblast?: [number, number] | boolean;
|
||||
sdmodel?: string;
|
||||
sdt5xxl?: string;
|
||||
sdclipl?: string;
|
||||
|
|
|
|||
393
src/stores/launchConfigStore.ts
Normal file
393
src/stores/launchConfigStore.ts
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
import { create } from 'zustand';
|
||||
import type { ConfigFile } from '@/types';
|
||||
import type { ImageModelPreset } from '@/utils/imageModelPresets';
|
||||
import {
|
||||
DEFAULT_CONTEXT_SIZE,
|
||||
DEFAULT_MODEL_URL,
|
||||
DEFAULT_HOST,
|
||||
} from '@/constants';
|
||||
|
||||
interface LaunchConfigState {
|
||||
gpuLayers: number;
|
||||
autoGpuLayers: boolean;
|
||||
contextSize: number;
|
||||
modelPath: string;
|
||||
additionalArguments: string;
|
||||
port?: number;
|
||||
host: string;
|
||||
multiuser: boolean;
|
||||
multiplayer: boolean;
|
||||
remotetunnel: boolean;
|
||||
nocertify: boolean;
|
||||
websearch: boolean;
|
||||
noshift: boolean;
|
||||
flashattention: boolean;
|
||||
noavx2: boolean;
|
||||
failsafe: boolean;
|
||||
lowvram: boolean;
|
||||
quantmatmul: boolean;
|
||||
backend: string;
|
||||
gpuDevice: number;
|
||||
gpuPlatform: number;
|
||||
sdmodel: string;
|
||||
sdt5xxl: string;
|
||||
sdclipl: string;
|
||||
sdclipg: string;
|
||||
sdphotomaker: string;
|
||||
sdvae: string;
|
||||
sdlora: string;
|
||||
|
||||
setGpuLayers: (layers: number) => void;
|
||||
setAutoGpuLayers: (auto: boolean) => void;
|
||||
setContextSize: (size: number) => void;
|
||||
setModelPath: (path: string) => void;
|
||||
setAdditionalArguments: (args: string) => void;
|
||||
setPort: (port?: number) => void;
|
||||
setHost: (host: string) => void;
|
||||
setMultiuser: (multiuser: boolean) => void;
|
||||
setMultiplayer: (multiplayer: boolean) => void;
|
||||
setRemotetunnel: (remotetunnel: boolean) => void;
|
||||
setNocertify: (nocertify: boolean) => void;
|
||||
setWebsearch: (websearch: boolean) => void;
|
||||
setNoshift: (noshift: boolean) => void;
|
||||
setFlashattention: (flashattention: boolean) => void;
|
||||
setNoavx2: (noavx2: boolean) => void;
|
||||
setFailsafe: (failsafe: boolean) => void;
|
||||
setLowvram: (lowvram: boolean) => void;
|
||||
setQuantmatmul: (quantmatmul: boolean) => void;
|
||||
setBackend: (backend: string) => void;
|
||||
setGpuDevice: (device: number) => void;
|
||||
setGpuPlatform: (platform: number) => void;
|
||||
setSdmodel: (model: string) => void;
|
||||
setSdt5xxl: (model: string) => void;
|
||||
setSdclipl: (model: string) => void;
|
||||
setSdclipg: (model: string) => void;
|
||||
setSdphotomaker: (model: string) => void;
|
||||
setSdvae: (vae: string) => void;
|
||||
setSdlora: (loraModel: string) => void;
|
||||
|
||||
parseAndApplyConfigFile: (configPath: string) => Promise<void>;
|
||||
loadConfigFromFile: (
|
||||
configFiles: ConfigFile[],
|
||||
savedConfig: string | null
|
||||
) => Promise<string | null>;
|
||||
selectModelFile: () => Promise<void>;
|
||||
selectSdmodelFile: () => Promise<void>;
|
||||
selectSdt5xxlFile: () => Promise<void>;
|
||||
selectSdcliplFile: () => Promise<void>;
|
||||
selectSdclipgFile: () => Promise<void>;
|
||||
selectSdphotomakerFile: () => Promise<void>;
|
||||
selectSdvaeFile: () => Promise<void>;
|
||||
selectSdloraFile: () => Promise<void>;
|
||||
contextSizeChangeWithStep: (size: number) => void;
|
||||
applyImageModelPreset: (preset: ImageModelPreset) => void;
|
||||
}
|
||||
|
||||
export const useLaunchConfigStore = create<LaunchConfigState>((set, get) => ({
|
||||
gpuLayers: 0,
|
||||
autoGpuLayers: false,
|
||||
contextSize: DEFAULT_CONTEXT_SIZE,
|
||||
modelPath: DEFAULT_MODEL_URL,
|
||||
additionalArguments: '',
|
||||
port: undefined,
|
||||
host: DEFAULT_HOST,
|
||||
multiuser: false,
|
||||
multiplayer: false,
|
||||
remotetunnel: false,
|
||||
nocertify: false,
|
||||
websearch: false,
|
||||
noshift: false,
|
||||
flashattention: true,
|
||||
noavx2: false,
|
||||
failsafe: false,
|
||||
lowvram: false,
|
||||
quantmatmul: true,
|
||||
backend: '',
|
||||
gpuDevice: 0,
|
||||
gpuPlatform: 0,
|
||||
sdmodel: '',
|
||||
sdt5xxl: '',
|
||||
sdclipl: '',
|
||||
sdclipg: '',
|
||||
sdphotomaker: '',
|
||||
sdvae: '',
|
||||
sdlora: '',
|
||||
|
||||
setGpuLayers: (layers) => set({ gpuLayers: layers }),
|
||||
setAutoGpuLayers: (auto) => set({ autoGpuLayers: auto }),
|
||||
setContextSize: (size) => set({ contextSize: size }),
|
||||
setModelPath: (path) => set({ modelPath: path }),
|
||||
setAdditionalArguments: (args) => set({ additionalArguments: args }),
|
||||
setPort: (port) => set({ port }),
|
||||
setHost: (host) => set({ host }),
|
||||
setMultiuser: (multiuser) => set({ multiuser }),
|
||||
setMultiplayer: (multiplayer) => set({ multiplayer }),
|
||||
setRemotetunnel: (remotetunnel) => set({ remotetunnel }),
|
||||
setNocertify: (nocertify) => set({ nocertify }),
|
||||
setWebsearch: (websearch) => set({ websearch }),
|
||||
setNoshift: (noshift) => set({ noshift }),
|
||||
setFlashattention: (flashattention) => set({ flashattention }),
|
||||
setNoavx2: (noavx2) => set({ noavx2 }),
|
||||
setFailsafe: (failsafe) => set({ failsafe }),
|
||||
setLowvram: (lowvram) => set({ lowvram }),
|
||||
setQuantmatmul: (quantmatmul) => set({ quantmatmul }),
|
||||
setBackend: (backend) => set({ backend }),
|
||||
setGpuDevice: (device) => set({ gpuDevice: device }),
|
||||
setGpuPlatform: (platform) => set({ gpuPlatform: platform }),
|
||||
setSdmodel: (model) => set({ sdmodel: model }),
|
||||
setSdt5xxl: (model) => set({ sdt5xxl: model }),
|
||||
setSdclipl: (model) => set({ sdclipl: model }),
|
||||
setSdclipg: (model) => set({ sdclipg: model }),
|
||||
setSdphotomaker: (model) => set({ sdphotomaker: model }),
|
||||
setSdvae: (vae) => set({ sdvae: vae }),
|
||||
setSdlora: (loraModel) => set({ sdlora: loraModel }),
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
parseAndApplyConfigFile: async (configPath: string) => {
|
||||
const configData =
|
||||
await window.electronAPI.kobold.parseConfigFile(configPath);
|
||||
|
||||
if (configData) {
|
||||
const updates: Partial<LaunchConfigState> = {};
|
||||
|
||||
if (typeof configData.gpulayers === 'number') {
|
||||
updates.gpuLayers = configData.gpulayers;
|
||||
} else {
|
||||
updates.gpuLayers = 0;
|
||||
}
|
||||
|
||||
if (typeof configData.contextsize === 'number') {
|
||||
updates.contextSize = configData.contextsize;
|
||||
} else {
|
||||
updates.contextSize = DEFAULT_CONTEXT_SIZE;
|
||||
}
|
||||
|
||||
if (typeof configData.model === 'string') {
|
||||
updates.modelPath = configData.model;
|
||||
}
|
||||
|
||||
if (typeof configData.port === 'number') {
|
||||
updates.port = configData.port;
|
||||
} else {
|
||||
updates.port = undefined;
|
||||
}
|
||||
|
||||
if (typeof configData.host === 'string') {
|
||||
updates.host = configData.host;
|
||||
} else {
|
||||
updates.host = DEFAULT_HOST;
|
||||
}
|
||||
|
||||
if (typeof configData.multiuser === 'number') {
|
||||
updates.multiuser = configData.multiuser === 1;
|
||||
} else {
|
||||
updates.multiuser = false;
|
||||
}
|
||||
|
||||
if (typeof configData.multiplayer === 'boolean') {
|
||||
updates.multiplayer = configData.multiplayer;
|
||||
} else {
|
||||
updates.multiplayer = false;
|
||||
}
|
||||
|
||||
if (typeof configData.remotetunnel === 'boolean') {
|
||||
updates.remotetunnel = configData.remotetunnel;
|
||||
} else {
|
||||
updates.remotetunnel = false;
|
||||
}
|
||||
|
||||
if (typeof configData.nocertify === 'boolean') {
|
||||
updates.nocertify = configData.nocertify;
|
||||
} else {
|
||||
updates.nocertify = false;
|
||||
}
|
||||
|
||||
if (typeof configData.websearch === 'boolean') {
|
||||
updates.websearch = configData.websearch;
|
||||
} else {
|
||||
updates.websearch = false;
|
||||
}
|
||||
|
||||
if (typeof configData.noshift === 'boolean') {
|
||||
updates.noshift = configData.noshift;
|
||||
} else {
|
||||
updates.noshift = false;
|
||||
}
|
||||
|
||||
if (typeof configData.flashattention === 'boolean') {
|
||||
updates.flashattention = configData.flashattention;
|
||||
} else {
|
||||
updates.flashattention = true;
|
||||
}
|
||||
|
||||
if (typeof configData.noavx2 === 'boolean') {
|
||||
updates.noavx2 = configData.noavx2;
|
||||
} else {
|
||||
updates.noavx2 = false;
|
||||
}
|
||||
|
||||
if (typeof configData.failsafe === 'boolean') {
|
||||
updates.failsafe = configData.failsafe;
|
||||
} else {
|
||||
updates.failsafe = false;
|
||||
}
|
||||
|
||||
if (typeof configData.lowvram === 'boolean') {
|
||||
updates.lowvram = configData.lowvram;
|
||||
}
|
||||
|
||||
if (typeof configData.quantmatmul === 'boolean') {
|
||||
updates.quantmatmul = configData.quantmatmul;
|
||||
}
|
||||
|
||||
if (configData.usecuda === true) {
|
||||
const gpuInfo = await window.electronAPI.kobold.detectGPU();
|
||||
updates.backend = gpuInfo.hasNVIDIA ? 'cuda' : 'rocm';
|
||||
|
||||
if (
|
||||
Array.isArray(configData.usecuda) &&
|
||||
configData.usecuda.length >= 3
|
||||
) {
|
||||
const [vramMode, deviceId, mmqMode] = configData.usecuda;
|
||||
updates.lowvram = vramMode === 'lowvram';
|
||||
updates.gpuDevice = parseInt(deviceId, 10) || 0;
|
||||
updates.quantmatmul = mmqMode === 'mmq';
|
||||
}
|
||||
} else if (configData.usevulkan === true) {
|
||||
updates.backend = 'vulkan';
|
||||
} else if (
|
||||
Array.isArray(configData.useclblast) &&
|
||||
configData.useclblast.length === 2
|
||||
) {
|
||||
updates.backend = 'clblast';
|
||||
const [deviceIndex, platformIndex] = configData.useclblast;
|
||||
updates.gpuDevice = deviceIndex;
|
||||
updates.gpuPlatform = platformIndex;
|
||||
} else {
|
||||
updates.backend = 'cpu';
|
||||
}
|
||||
|
||||
if (typeof configData.sdmodel === 'string') {
|
||||
updates.sdmodel = configData.sdmodel;
|
||||
}
|
||||
|
||||
if (typeof configData.sdt5xxl === 'string') {
|
||||
updates.sdt5xxl = configData.sdt5xxl;
|
||||
}
|
||||
|
||||
if (typeof configData.sdclipl === 'string') {
|
||||
updates.sdclipl = configData.sdclipl;
|
||||
}
|
||||
|
||||
if (typeof configData.sdclipg === 'string') {
|
||||
updates.sdclipg = configData.sdclipg;
|
||||
}
|
||||
|
||||
if (typeof configData.sdphotomaker === 'string') {
|
||||
updates.sdphotomaker = configData.sdphotomaker;
|
||||
}
|
||||
|
||||
if (typeof configData.sdvae === 'string') {
|
||||
updates.sdvae = configData.sdvae;
|
||||
}
|
||||
|
||||
if (typeof configData.sdlora === 'string') {
|
||||
updates.sdlora = configData.sdlora;
|
||||
}
|
||||
|
||||
set(updates);
|
||||
}
|
||||
},
|
||||
|
||||
loadConfigFromFile: async (
|
||||
configFiles: ConfigFile[],
|
||||
savedConfig: string | null
|
||||
): Promise<string | null> => {
|
||||
let currentSelectedFile = null;
|
||||
|
||||
if (savedConfig) {
|
||||
currentSelectedFile = configFiles.find((f) => f.name === savedConfig);
|
||||
}
|
||||
|
||||
if (!currentSelectedFile && configFiles.length > 0) {
|
||||
currentSelectedFile = configFiles[0];
|
||||
}
|
||||
|
||||
if (currentSelectedFile) {
|
||||
await get().parseAndApplyConfigFile(currentSelectedFile.path);
|
||||
return currentSelectedFile.name;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
selectModelFile: async () => {
|
||||
const result = await window.electronAPI.kobold.selectModelFile();
|
||||
if (result) {
|
||||
set({ modelPath: result });
|
||||
}
|
||||
},
|
||||
|
||||
selectSdmodelFile: async () => {
|
||||
const result = await window.electronAPI.kobold.selectModelFile();
|
||||
if (result) {
|
||||
set({ sdmodel: result });
|
||||
}
|
||||
},
|
||||
|
||||
selectSdt5xxlFile: async () => {
|
||||
const result = await window.electronAPI.kobold.selectModelFile();
|
||||
if (result) {
|
||||
set({ sdt5xxl: result });
|
||||
}
|
||||
},
|
||||
|
||||
selectSdcliplFile: async () => {
|
||||
const result = await window.electronAPI.kobold.selectModelFile();
|
||||
if (result) {
|
||||
set({ sdclipl: result });
|
||||
}
|
||||
},
|
||||
|
||||
selectSdclipgFile: async () => {
|
||||
const result = await window.electronAPI.kobold.selectModelFile();
|
||||
if (result) {
|
||||
set({ sdclipg: result });
|
||||
}
|
||||
},
|
||||
|
||||
selectSdphotomakerFile: async () => {
|
||||
const result = await window.electronAPI.kobold.selectModelFile();
|
||||
if (result) {
|
||||
set({ sdphotomaker: result });
|
||||
}
|
||||
},
|
||||
|
||||
selectSdvaeFile: async () => {
|
||||
const result = await window.electronAPI.kobold.selectModelFile();
|
||||
if (result) {
|
||||
set({ sdvae: result });
|
||||
}
|
||||
},
|
||||
|
||||
selectSdloraFile: async () => {
|
||||
const result = await window.electronAPI.kobold.selectModelFile();
|
||||
if (result) {
|
||||
set({ sdlora: result });
|
||||
}
|
||||
},
|
||||
|
||||
contextSizeChangeWithStep: (size: number) => {
|
||||
const roundedSize = Math.round(size / 256) * 256;
|
||||
set({ contextSize: Math.max(256, Math.min(131072, roundedSize)) });
|
||||
},
|
||||
|
||||
applyImageModelPreset: (preset: ImageModelPreset) => {
|
||||
set({
|
||||
sdt5xxl: preset.sdt5xxl,
|
||||
sdclipl: preset.sdclipl,
|
||||
sdclipg: preset.sdclipg || '',
|
||||
sdvae: preset.sdvae,
|
||||
});
|
||||
},
|
||||
}));
|
||||
6
src/types/electron.d.ts
vendored
6
src/types/electron.d.ts
vendored
|
|
@ -110,7 +110,7 @@ export interface KoboldAPI {
|
|||
configData: {
|
||||
gpulayers?: number;
|
||||
contextsize?: number;
|
||||
model_param?: string;
|
||||
model?: string;
|
||||
port?: number;
|
||||
host?: string;
|
||||
multiuser?: number;
|
||||
|
|
@ -124,7 +124,7 @@ export interface KoboldAPI {
|
|||
failsafe?: boolean;
|
||||
usecuda?: boolean;
|
||||
usevulkan?: boolean;
|
||||
useclblast?: boolean;
|
||||
useclblast?: [number, number] | boolean;
|
||||
sdmodel?: string;
|
||||
sdt5xxl?: string;
|
||||
sdclipl?: string;
|
||||
|
|
@ -139,7 +139,7 @@ export interface KoboldAPI {
|
|||
parseConfigFile: (filePath: string) => Promise<{
|
||||
gpulayers?: number;
|
||||
contextsize?: number;
|
||||
model_param?: string;
|
||||
model?: string;
|
||||
[key: string]: unknown;
|
||||
} | null>;
|
||||
selectModelFile: () => Promise<string | null>;
|
||||
|
|
|
|||
25
src/utils/clblast.ts
Normal file
25
src/utils/clblast.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
export function parseCLBlastDevice(deviceString: string): {
|
||||
deviceIndex: number;
|
||||
platformIndex: number;
|
||||
} | null {
|
||||
const match = deviceString.match(/\[(\d+),(\d+)\]$/);
|
||||
if (match) {
|
||||
return {
|
||||
deviceIndex: parseInt(match[1], 10),
|
||||
platformIndex: parseInt(match[2], 10),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function formatCLBlastArgs(
|
||||
deviceIndex: number,
|
||||
platformIndex: number
|
||||
): [string, string] {
|
||||
return [deviceIndex.toString(), platformIndex.toString()];
|
||||
}
|
||||
|
||||
export function getCLBlastDeviceName(deviceString: string): string {
|
||||
const match = deviceString.match(/^(.+?)\s+\(Platform:/);
|
||||
return match ? match[1].trim() : deviceString;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export * from './assets';
|
||||
export * from './clblast';
|
||||
export * from './downloadUtils';
|
||||
export * from './fileSize';
|
||||
export * from './hardware';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue