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",
|
"KOBOLDAI",
|
||||||
"koboldcpp",
|
"koboldcpp",
|
||||||
"KoboldCpp",
|
"KoboldCpp",
|
||||||
|
"lora",
|
||||||
"lowvram",
|
"lowvram",
|
||||||
"Lowvram",
|
"Lowvram",
|
||||||
"maximizable",
|
"maximizable",
|
||||||
|
|
@ -50,6 +51,7 @@
|
||||||
"nvidia",
|
"nvidia",
|
||||||
"oldpc",
|
"oldpc",
|
||||||
"OLDPC",
|
"OLDPC",
|
||||||
|
"opencl",
|
||||||
"Papi",
|
"Papi",
|
||||||
"Philippov",
|
"Philippov",
|
||||||
"pkexec",
|
"pkexec",
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,11 @@
|
||||||
"@mantine/core": "^8.2.5",
|
"@mantine/core": "^8.2.5",
|
||||||
"@mantine/hooks": "^8.2.5",
|
"@mantine/hooks": "^8.2.5",
|
||||||
"lucide-react": "^0.540.0",
|
"lucide-react": "^0.540.0",
|
||||||
|
"opencl-info": "^0.3.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"systeminformation": "^5.27.7"
|
"systeminformation": "^5.27.7",
|
||||||
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "com.friendly-kobold.app",
|
"appId": "com.friendly-kobold.app",
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import styles from '@/styles/layout.module.css';
|
||||||
interface ConfigFileManagerProps {
|
interface ConfigFileManagerProps {
|
||||||
configFiles: ConfigFile[];
|
configFiles: ConfigFile[];
|
||||||
selectedFile: string | null;
|
selectedFile: string | null;
|
||||||
hasUnsavedChanges: boolean;
|
|
||||||
onFileSelection: (fileName: string) => Promise<void>;
|
onFileSelection: (fileName: string) => Promise<void>;
|
||||||
onCreateNewConfig: (configName: string) => Promise<void>;
|
onCreateNewConfig: (configName: string) => Promise<void>;
|
||||||
onSaveConfig: () => void;
|
onSaveConfig: () => void;
|
||||||
|
|
@ -64,7 +63,6 @@ SelectItem.displayName = 'SelectItem';
|
||||||
export const ConfigFileManager = ({
|
export const ConfigFileManager = ({
|
||||||
configFiles,
|
configFiles,
|
||||||
selectedFile,
|
selectedFile,
|
||||||
hasUnsavedChanges,
|
|
||||||
onFileSelection,
|
onFileSelection,
|
||||||
onCreateNewConfig,
|
onCreateNewConfig,
|
||||||
onSaveConfig,
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
|
|
@ -127,11 +116,7 @@ export const ConfigFileManager = ({
|
||||||
<div className={styles.flex1}>
|
<div className={styles.flex1}>
|
||||||
<Select
|
<Select
|
||||||
placeholder="Select a configuration file"
|
placeholder="Select a configuration file"
|
||||||
value={
|
value={selectedFile}
|
||||||
selectedFile === null && hasUnsavedChanges
|
|
||||||
? '__new__'
|
|
||||||
: selectedFile
|
|
||||||
}
|
|
||||||
onChange={(value: string | null) => {
|
onChange={(value: string | null) => {
|
||||||
if (value === '__new__') {
|
if (value === '__new__') {
|
||||||
return;
|
return;
|
||||||
|
|
@ -156,24 +141,18 @@ export const ConfigFileManager = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant={
|
variant="light"
|
||||||
selectedFile === null && hasUnsavedChanges ? 'filled' : 'light'
|
|
||||||
}
|
|
||||||
leftSection={<Plus size={14} />}
|
leftSection={<Plus size={14} />}
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={selectedFile === null && hasUnsavedChanges}
|
|
||||||
onClick={() => handleOpenConfigModal()}
|
onClick={() => handleOpenConfigModal()}
|
||||||
>
|
>
|
||||||
{selectedFile === null && hasUnsavedChanges
|
New
|
||||||
? 'Creating New...'
|
|
||||||
: 'New'}
|
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
leftSection={<Save size={14} />}
|
leftSection={<Save size={14} />}
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={!hasUnsavedChanges}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (selectedFile) {
|
if (selectedFile) {
|
||||||
onSaveConfig();
|
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 { useState, useEffect } from 'react';
|
||||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||||
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
import styles from '@/styles/layout.module.css';
|
import styles from '@/styles/layout.module.css';
|
||||||
|
|
||||||
interface AdvancedTabProps {
|
export const AdvancedTab = () => {
|
||||||
additionalArguments: string;
|
const {
|
||||||
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 = ({
|
|
||||||
additionalArguments,
|
additionalArguments,
|
||||||
noshift,
|
noshift,
|
||||||
flashattention,
|
flashattention,
|
||||||
|
|
@ -30,14 +14,14 @@ export const AdvancedTab = ({
|
||||||
lowvram,
|
lowvram,
|
||||||
quantmatmul,
|
quantmatmul,
|
||||||
backend,
|
backend,
|
||||||
onAdditionalArgumentsChange,
|
handleAdditionalArgumentsChange,
|
||||||
onNoshiftChange,
|
handleNoshiftChange,
|
||||||
onFlashattentionChange,
|
handleFlashattentionChange,
|
||||||
onNoavx2Change,
|
handleNoavx2Change,
|
||||||
onFailsafeChange,
|
handleFailsafeChange,
|
||||||
onLowvramChange,
|
handleLowvramChange,
|
||||||
onQuantmatmulChange,
|
handleQuantmatmulChange,
|
||||||
}: AdvancedTabProps) => {
|
} = useLaunchConfig();
|
||||||
const [backendSupport, setBackendSupport] = useState<{
|
const [backendSupport, setBackendSupport] = useState<{
|
||||||
noavx2: boolean;
|
noavx2: boolean;
|
||||||
failsafe: boolean;
|
failsafe: boolean;
|
||||||
|
|
@ -87,7 +71,7 @@ export const AdvancedTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={!noshift}
|
checked={!noshift}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onNoshiftChange(!event.currentTarget.checked)
|
handleNoshiftChange(!event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Context Shift"
|
label="Context Shift"
|
||||||
/>
|
/>
|
||||||
|
|
@ -100,7 +84,7 @@ export const AdvancedTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={noshift}
|
checked={noshift}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onNoshiftChange(event.currentTarget.checked)
|
handleNoshiftChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="No Shift"
|
label="No Shift"
|
||||||
/>
|
/>
|
||||||
|
|
@ -115,7 +99,7 @@ export const AdvancedTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={flashattention}
|
checked={flashattention}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onFlashattentionChange(event.currentTarget.checked)
|
handleFlashattentionChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Flash Attention"
|
label="Flash Attention"
|
||||||
/>
|
/>
|
||||||
|
|
@ -128,7 +112,7 @@ export const AdvancedTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={lowvram}
|
checked={lowvram}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onLowvramChange(event.currentTarget.checked)
|
handleLowvramChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Low VRAM"
|
label="Low VRAM"
|
||||||
disabled={backend !== 'cuda' && backend !== 'rocm'}
|
disabled={backend !== 'cuda' && backend !== 'rocm'}
|
||||||
|
|
@ -150,7 +134,7 @@ export const AdvancedTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={quantmatmul}
|
checked={quantmatmul}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onQuantmatmulChange(event.currentTarget.checked)
|
handleQuantmatmulChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="QuantMatMul"
|
label="QuantMatMul"
|
||||||
disabled={backend !== 'cuda' && backend !== 'rocm'}
|
disabled={backend !== 'cuda' && backend !== 'rocm'}
|
||||||
|
|
@ -181,7 +165,7 @@ export const AdvancedTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={noavx2}
|
checked={noavx2}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onNoavx2Change(event.currentTarget.checked)
|
handleNoavx2Change(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Disable AVX2"
|
label="Disable AVX2"
|
||||||
disabled={isLoading || !backendSupport?.noavx2}
|
disabled={isLoading || !backendSupport?.noavx2}
|
||||||
|
|
@ -201,7 +185,7 @@ export const AdvancedTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={failsafe}
|
checked={failsafe}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onFailsafeChange(event.currentTarget.checked)
|
handleFailsafeChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Failsafe"
|
label="Failsafe"
|
||||||
disabled={isLoading || !backendSupport?.failsafe}
|
disabled={isLoading || !backendSupport?.failsafe}
|
||||||
|
|
@ -230,7 +214,7 @@ export const AdvancedTab = ({
|
||||||
placeholder="Additional command line arguments"
|
placeholder="Additional command line arguments"
|
||||||
value={additionalArguments}
|
value={additionalArguments}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onAdditionalArgumentsChange(event.currentTarget.value)
|
handleAdditionalArgumentsChange(event.currentTarget.value)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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 { useState, useEffect, useRef } from 'react';
|
||||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||||
import { BackendSelectItem } from '@/components/screens/Launch/GeneralTab/BackendSelectItem';
|
import { BackendSelectItem } from '@/components/screens/Launch/GeneralTab/BackendSelectItem';
|
||||||
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
|
|
||||||
interface BackendSelectorProps {
|
interface BackendSelectorProps {
|
||||||
backend: string;
|
|
||||||
onBackendChange: (backend: string) => void;
|
|
||||||
gpuDevice?: number;
|
|
||||||
onGpuDeviceChange?: (device: number) => void;
|
|
||||||
noavx2?: boolean;
|
|
||||||
failsafe?: boolean;
|
|
||||||
onWarningsChange?: (
|
onWarningsChange?: (
|
||||||
warnings: Array<{ type: 'warning' | 'info'; message: string }>
|
warnings: Array<{ type: 'warning' | 'info'; message: string }>
|
||||||
) => void;
|
) => void;
|
||||||
onBackendsReady?: () => void;
|
onBackendsReady?: () => void;
|
||||||
gpuLayers?: number;
|
|
||||||
autoGpuLayers?: boolean;
|
|
||||||
onGpuLayersChange?: (layers: number) => void;
|
|
||||||
onAutoGpuLayersChange?: (auto: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BackendSelector = ({
|
export const BackendSelector = ({
|
||||||
backend,
|
|
||||||
onBackendChange,
|
|
||||||
gpuDevice = 0,
|
|
||||||
onGpuDeviceChange,
|
|
||||||
noavx2 = false,
|
|
||||||
failsafe = false,
|
|
||||||
onWarningsChange,
|
onWarningsChange,
|
||||||
onBackendsReady,
|
onBackendsReady,
|
||||||
gpuLayers = 0,
|
|
||||||
autoGpuLayers = false,
|
|
||||||
onGpuLayersChange,
|
|
||||||
onAutoGpuLayersChange,
|
|
||||||
}: BackendSelectorProps) => {
|
}: BackendSelectorProps) => {
|
||||||
|
const {
|
||||||
|
backend,
|
||||||
|
gpuDevice,
|
||||||
|
noavx2,
|
||||||
|
failsafe,
|
||||||
|
gpuLayers,
|
||||||
|
autoGpuLayers,
|
||||||
|
handleBackendChange,
|
||||||
|
handleGpuDeviceChange,
|
||||||
|
handleGpuLayersChange,
|
||||||
|
handleAutoGpuLayersChange,
|
||||||
|
} = useLaunchConfig();
|
||||||
|
|
||||||
const [availableBackends, setAvailableBackends] = useState<
|
const [availableBackends, setAvailableBackends] = useState<
|
||||||
Array<{ value: string; label: string; devices?: string[] }>
|
Array<{ value: string; label: string; devices?: string[] }>
|
||||||
>([]);
|
>([]);
|
||||||
|
|
@ -44,10 +38,6 @@ export const BackendSelector = ({
|
||||||
const hasInitialized = useRef(false);
|
const hasInitialized = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasInitialized.current) return;
|
|
||||||
|
|
||||||
let timeoutId: number;
|
|
||||||
|
|
||||||
const loadBackends = async () => {
|
const loadBackends = async () => {
|
||||||
try {
|
try {
|
||||||
const [currentBinaryInfo, cpuCapabilitiesResult, gpuCapabilities] =
|
const [currentBinaryInfo, cpuCapabilitiesResult, gpuCapabilities] =
|
||||||
|
|
@ -83,16 +73,8 @@ export const BackendSelector = ({
|
||||||
setAvailableBackends(backends);
|
setAvailableBackends(backends);
|
||||||
hasInitialized.current = true;
|
hasInitialized.current = true;
|
||||||
|
|
||||||
if (backends.length > 0 && !backend) {
|
|
||||||
timeoutId = window.setTimeout(() => {
|
|
||||||
onBackendChange(backends[0].value);
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onBackendsReady) {
|
if (onBackendsReady) {
|
||||||
window.setTimeout(() => {
|
|
||||||
onBackendsReady();
|
onBackendsReady();
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.electronAPI.logs.logError(
|
window.electronAPI.logs.logError(
|
||||||
|
|
@ -107,14 +89,10 @@ export const BackendSelector = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!hasInitialized.current) {
|
||||||
loadBackends();
|
loadBackends();
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (timeoutId) {
|
|
||||||
window.clearTimeout(timeoutId);
|
|
||||||
}
|
}
|
||||||
};
|
}, [onBackendsReady]);
|
||||||
}, [backend, onBackendChange, onBackendsReady]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!onWarningsChange) return;
|
if (!onWarningsChange) return;
|
||||||
|
|
@ -130,7 +108,7 @@ export const BackendSelector = ({
|
||||||
warnings.push({
|
warnings.push({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message:
|
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({
|
warnings.push({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message:
|
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}
|
value={backend}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
onBackendChange(value);
|
handleBackendChange(value);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
data={availableBackends.map((b) => ({
|
data={availableBackends.map((b) => ({
|
||||||
|
|
@ -186,11 +164,6 @@ export const BackendSelector = ({
|
||||||
label: b.label,
|
label: b.label,
|
||||||
}))}
|
}))}
|
||||||
disabled={availableBackends.length === 0}
|
disabled={availableBackends.length === 0}
|
||||||
comboboxProps={{
|
|
||||||
middlewares: {
|
|
||||||
flip: false,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
renderOption={({ option }) => {
|
renderOption={({ option }) => {
|
||||||
const backendData = availableBackends.find(
|
const backendData = availableBackends.find(
|
||||||
(b) => b.value === option.value
|
(b) => b.value === option.value
|
||||||
|
|
@ -213,14 +186,13 @@ export const BackendSelector = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isGpuBackend &&
|
isGpuBackend &&
|
||||||
onGpuDeviceChange &&
|
|
||||||
hasMultipleDevices && (
|
hasMultipleDevices && (
|
||||||
<Select
|
<Select
|
||||||
label="GPU Device"
|
label="GPU Device"
|
||||||
placeholder="Select GPU device"
|
placeholder="Select GPU device"
|
||||||
value={gpuDevice.toString()}
|
value={gpuDevice.toString()}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
value && onGpuDeviceChange(parseInt(value, 10))
|
value && handleGpuDeviceChange(parseInt(value, 10))
|
||||||
}
|
}
|
||||||
data={selectedBackend.devices!.map((device, index) => ({
|
data={selectedBackend.devices!.map((device, index) => ({
|
||||||
value: index.toString(),
|
value: index.toString(),
|
||||||
|
|
@ -233,7 +205,6 @@ export const BackendSelector = ({
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{onGpuLayersChange && onAutoGpuLayersChange && (
|
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<Group gap="xs" align="center" mb="xs">
|
<Group gap="xs" align="center" mb="xs">
|
||||||
<Text size="sm" fw={500}>
|
<Text size="sm" fw={500}>
|
||||||
|
|
@ -245,7 +216,7 @@ export const BackendSelector = ({
|
||||||
<TextInput
|
<TextInput
|
||||||
value={gpuLayers.toString()}
|
value={gpuLayers.toString()}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onGpuLayersChange(Number(event.target.value) || 0)
|
handleGpuLayersChange(Number(event.target.value) || 0)
|
||||||
}
|
}
|
||||||
type="number"
|
type="number"
|
||||||
min={0}
|
min={0}
|
||||||
|
|
@ -260,7 +231,7 @@ export const BackendSelector = ({
|
||||||
label="Auto"
|
label="Auto"
|
||||||
checked={autoGpuLayers}
|
checked={autoGpuLayers}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onAutoGpuLayersChange(event.currentTarget.checked)
|
handleAutoGpuLayersChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
|
|
@ -268,7 +239,6 @@ export const BackendSelector = ({
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</Group>
|
</Group>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,10 @@ import { File, Search } from 'lucide-react';
|
||||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||||
import { BackendSelector } from '@/components/screens/Launch/GeneralTab/BackendSelector';
|
import { BackendSelector } from '@/components/screens/Launch/GeneralTab/BackendSelector';
|
||||||
import { getInputValidationState } from '@/utils';
|
import { getInputValidationState } from '@/utils';
|
||||||
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
import styles from '@/styles/layout.module.css';
|
import styles from '@/styles/layout.module.css';
|
||||||
|
|
||||||
interface GeneralTabProps {
|
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?: (
|
onWarningsChange?: (
|
||||||
warnings: Array<{ type: 'warning' | 'info'; message: string }>
|
warnings: Array<{ type: 'warning' | 'info'; message: string }>
|
||||||
) => void;
|
) => void;
|
||||||
|
|
@ -28,24 +14,17 @@ interface GeneralTabProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GeneralTab = ({
|
export const GeneralTab = ({
|
||||||
modelPath,
|
|
||||||
gpuLayers,
|
|
||||||
autoGpuLayers,
|
|
||||||
contextSize,
|
|
||||||
backend,
|
|
||||||
gpuDevice,
|
|
||||||
noavx2,
|
|
||||||
failsafe,
|
|
||||||
onModelPathChange,
|
|
||||||
onSelectModelFile,
|
|
||||||
onGpuLayersChange,
|
|
||||||
onAutoGpuLayersChange,
|
|
||||||
onContextSizeChange,
|
|
||||||
onBackendChange,
|
|
||||||
onGpuDeviceChange,
|
|
||||||
onWarningsChange,
|
onWarningsChange,
|
||||||
onBackendsReady,
|
onBackendsReady,
|
||||||
}: GeneralTabProps) => {
|
}: GeneralTabProps) => {
|
||||||
|
const {
|
||||||
|
modelPath,
|
||||||
|
contextSize,
|
||||||
|
handleModelPathChange,
|
||||||
|
handleSelectModelFile,
|
||||||
|
handleContextSizeChangeWithStep,
|
||||||
|
} = useLaunchConfig();
|
||||||
|
|
||||||
const validationState = getInputValidationState(modelPath);
|
const validationState = getInputValidationState(modelPath);
|
||||||
|
|
||||||
const getInputColor = () => {
|
const getInputColor = () => {
|
||||||
|
|
@ -72,18 +51,8 @@ export const GeneralTab = ({
|
||||||
return (
|
return (
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<BackendSelector
|
<BackendSelector
|
||||||
backend={backend}
|
|
||||||
onBackendChange={onBackendChange}
|
|
||||||
gpuDevice={gpuDevice}
|
|
||||||
onGpuDeviceChange={onGpuDeviceChange}
|
|
||||||
noavx2={noavx2}
|
|
||||||
failsafe={failsafe}
|
|
||||||
onWarningsChange={onWarningsChange}
|
onWarningsChange={onWarningsChange}
|
||||||
onBackendsReady={onBackendsReady}
|
onBackendsReady={onBackendsReady}
|
||||||
gpuLayers={gpuLayers}
|
|
||||||
autoGpuLayers={autoGpuLayers}
|
|
||||||
onGpuLayersChange={onGpuLayersChange}
|
|
||||||
onAutoGpuLayersChange={onAutoGpuLayersChange}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -95,7 +64,7 @@ export const GeneralTab = ({
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Select a .gguf model file or enter a direct URL to file"
|
placeholder="Select a .gguf model file or enter a direct URL to file"
|
||||||
value={modelPath}
|
value={modelPath}
|
||||||
onChange={(event) => onModelPathChange(event.currentTarget.value)}
|
onChange={(e) => handleModelPathChange(e.target.value)}
|
||||||
color={getInputColor()}
|
color={getInputColor()}
|
||||||
error={
|
error={
|
||||||
validationState === 'invalid' ? getHelperText() : undefined
|
validationState === 'invalid' ? getHelperText() : undefined
|
||||||
|
|
@ -103,7 +72,7 @@ export const GeneralTab = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onClick={onSelectModelFile}
|
onClick={handleSelectModelFile}
|
||||||
variant="light"
|
variant="light"
|
||||||
leftSection={<File size={16} />}
|
leftSection={<File size={16} />}
|
||||||
>
|
>
|
||||||
|
|
@ -134,7 +103,7 @@ export const GeneralTab = ({
|
||||||
<TextInput
|
<TextInput
|
||||||
value={contextSize?.toString() || ''}
|
value={contextSize?.toString() || ''}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onContextSizeChange(Number(event.target.value) || 256)
|
handleContextSizeChangeWithStep(Number(event.target.value) || 256)
|
||||||
}
|
}
|
||||||
type="number"
|
type="number"
|
||||||
min={256}
|
min={256}
|
||||||
|
|
@ -149,7 +118,7 @@ export const GeneralTab = ({
|
||||||
min={256}
|
min={256}
|
||||||
max={131072}
|
max={131072}
|
||||||
step={1}
|
step={1}
|
||||||
onChange={onContextSizeChange}
|
onChange={handleContextSizeChangeWithStep}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
|
||||||
|
|
@ -3,32 +3,36 @@ import { useState } from 'react';
|
||||||
import { File, Search } from 'lucide-react';
|
import { File, Search } from 'lucide-react';
|
||||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||||
import { getInputValidationState, IMAGE_MODEL_PRESETS } from '@/utils';
|
import { getInputValidationState, IMAGE_MODEL_PRESETS } from '@/utils';
|
||||||
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
import styles from '@/styles/layout.module.css';
|
import styles from '@/styles/layout.module.css';
|
||||||
|
|
||||||
interface ImageGenerationTabProps {
|
export const ImageGenerationTab = () => {
|
||||||
sdmodel: string;
|
const {
|
||||||
sdt5xxl: string;
|
sdmodel,
|
||||||
sdclipl: string;
|
sdt5xxl,
|
||||||
sdclipg: string;
|
sdclipl,
|
||||||
sdphotomaker: string;
|
sdclipg,
|
||||||
sdvae: string;
|
sdphotomaker,
|
||||||
sdlora: string;
|
sdvae,
|
||||||
onSdmodelChange: (path: string) => void;
|
sdlora,
|
||||||
onSelectSdmodelFile: () => void;
|
handleSdmodelChange,
|
||||||
onSdt5xxlChange: (path: string) => void;
|
handleSelectSdmodelFile,
|
||||||
onSelectSdt5xxlFile: () => void;
|
handleSdt5xxlChange,
|
||||||
onSdcliplChange: (path: string) => void;
|
handleSelectSdt5xxlFile,
|
||||||
onSelectSdcliplFile: () => void;
|
handleSdcliplChange,
|
||||||
onSdclipgChange: (path: string) => void;
|
handleSelectSdcliplFile,
|
||||||
onSelectSdclipgFile: () => void;
|
handleSdclipgChange,
|
||||||
onSdphotomakerChange: (path: string) => void;
|
handleSelectSdclipgFile,
|
||||||
onSelectSdphotomakerFile: () => void;
|
handleSdphotomakerChange,
|
||||||
onSdvaeChange: (path: string) => void;
|
handleSelectSdphotomakerFile,
|
||||||
onSelectSdvaeFile: () => void;
|
handleSdvaeChange,
|
||||||
onSdloraChange: (path: string) => void;
|
handleSelectSdvaeFile,
|
||||||
onSelectSdloraFile: () => void;
|
handleSdloraChange,
|
||||||
onApplyPreset: (presetName: string) => void;
|
handleSelectSdloraFile,
|
||||||
}
|
handleApplyPreset,
|
||||||
|
} = useLaunchConfig();
|
||||||
|
|
||||||
|
const [selectedPreset, setSelectedPreset] = useState<string | null>(null);
|
||||||
|
|
||||||
const ModelField = ({
|
const ModelField = ({
|
||||||
label,
|
label,
|
||||||
|
|
@ -85,7 +89,9 @@ const ModelField = ({
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(event) => onChange(event.currentTarget.value)}
|
onChange={(event) => onChange(event.currentTarget.value)}
|
||||||
color={getInputColor()}
|
color={getInputColor()}
|
||||||
error={validationState === 'invalid' ? getHelperText() : undefined}
|
error={
|
||||||
|
validationState === 'invalid' ? getHelperText() : undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<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 (
|
return (
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -158,7 +138,7 @@ export const ImageGenerationTab = ({
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setSelectedPreset(value);
|
setSelectedPreset(value);
|
||||||
if (value) {
|
if (value) {
|
||||||
onApplyPreset(value);
|
handleApplyPreset(value);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
clearable
|
clearable
|
||||||
|
|
@ -169,8 +149,8 @@ export const ImageGenerationTab = ({
|
||||||
label="Image Gen. Model File"
|
label="Image Gen. Model File"
|
||||||
value={sdmodel}
|
value={sdmodel}
|
||||||
placeholder="Select a model file or enter a direct URL"
|
placeholder="Select a model file or enter a direct URL"
|
||||||
onChange={onSdmodelChange}
|
onChange={handleSdmodelChange}
|
||||||
onSelectFile={onSelectSdmodelFile}
|
onSelectFile={handleSelectSdmodelFile}
|
||||||
showSearchHF
|
showSearchHF
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -178,24 +158,24 @@ export const ImageGenerationTab = ({
|
||||||
label="T5-XXL File"
|
label="T5-XXL File"
|
||||||
value={sdt5xxl}
|
value={sdt5xxl}
|
||||||
placeholder="Select a T5-XXL file or enter a direct URL"
|
placeholder="Select a T5-XXL file or enter a direct URL"
|
||||||
onChange={onSdt5xxlChange}
|
onChange={handleSdt5xxlChange}
|
||||||
onSelectFile={onSelectSdt5xxlFile}
|
onSelectFile={handleSelectSdt5xxlFile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ModelField
|
<ModelField
|
||||||
label="Clip-L File"
|
label="Clip-L File"
|
||||||
value={sdclipl}
|
value={sdclipl}
|
||||||
placeholder="Select a Clip-L file or enter a direct URL"
|
placeholder="Select a Clip-L file or enter a direct URL"
|
||||||
onChange={onSdcliplChange}
|
onChange={handleSdcliplChange}
|
||||||
onSelectFile={onSelectSdcliplFile}
|
onSelectFile={handleSelectSdcliplFile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ModelField
|
<ModelField
|
||||||
label="Clip-G File"
|
label="Clip-G File"
|
||||||
value={sdclipg}
|
value={sdclipg}
|
||||||
placeholder="Select a Clip-G file or enter a direct URL"
|
placeholder="Select a Clip-G file or enter a direct URL"
|
||||||
onChange={onSdclipgChange}
|
onChange={handleSdclipgChange}
|
||||||
onSelectFile={onSelectSdclipgFile}
|
onSelectFile={handleSelectSdclipgFile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ModelField
|
<ModelField
|
||||||
|
|
@ -203,16 +183,16 @@ export const ImageGenerationTab = ({
|
||||||
value={sdphotomaker}
|
value={sdphotomaker}
|
||||||
placeholder="Select a PhotoMaker file or enter a direct URL"
|
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)."
|
tooltip="PhotoMaker is a model that allows face cloning. Select a .safetensors PhotoMaker file to be loaded (SDXL only)."
|
||||||
onChange={onSdphotomakerChange}
|
onChange={handleSdphotomakerChange}
|
||||||
onSelectFile={onSelectSdphotomakerFile}
|
onSelectFile={handleSelectSdphotomakerFile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ModelField
|
<ModelField
|
||||||
label="Image VAE"
|
label="Image VAE"
|
||||||
value={sdvae}
|
value={sdvae}
|
||||||
placeholder="Select a VAE file or enter a direct URL"
|
placeholder="Select a VAE file or enter a direct URL"
|
||||||
onChange={onSdvaeChange}
|
onChange={handleSdvaeChange}
|
||||||
onSelectFile={onSelectSdvaeFile}
|
onSelectFile={handleSelectSdvaeFile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ModelField
|
<ModelField
|
||||||
|
|
@ -220,8 +200,8 @@ export const ImageGenerationTab = ({
|
||||||
value={sdlora}
|
value={sdlora}
|
||||||
placeholder="Select a LoRa file or enter a direct URL"
|
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."
|
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}
|
onChange={handleSdloraChange}
|
||||||
onSelectFile={onSelectSdloraFile}
|
onSelectFile={handleSelectSdloraFile}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,11 @@
|
||||||
import { Stack, Text, TextInput, Group, Checkbox } from '@mantine/core';
|
import { Stack, Text, TextInput, Group, Checkbox } from '@mantine/core';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||||
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
import styles from '@/styles/layout.module.css';
|
import styles from '@/styles/layout.module.css';
|
||||||
|
|
||||||
interface NetworkTabProps {
|
export const NetworkTab = () => {
|
||||||
port: number | undefined;
|
const {
|
||||||
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 = ({
|
|
||||||
port,
|
port,
|
||||||
host,
|
host,
|
||||||
multiuser,
|
multiuser,
|
||||||
|
|
@ -28,14 +13,14 @@ export const NetworkTab = ({
|
||||||
remotetunnel,
|
remotetunnel,
|
||||||
nocertify,
|
nocertify,
|
||||||
websearch,
|
websearch,
|
||||||
onPortChange,
|
handlePortChange,
|
||||||
onHostChange,
|
handleHostChange,
|
||||||
onMultiuserChange,
|
handleMultiuserChange,
|
||||||
onMultiplayerChange,
|
handleMultiplayerChange,
|
||||||
onRemotetunnelChange,
|
handleRemotetunnelChange,
|
||||||
onNocertifyChange,
|
handleNocertifyChange,
|
||||||
onWebsearchChange,
|
handleWebsearchChange,
|
||||||
}: NetworkTabProps) => {
|
} = useLaunchConfig();
|
||||||
const [portInput, setPortInput] = useState(port?.toString() ?? '');
|
const [portInput, setPortInput] = useState(port?.toString() ?? '');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -53,10 +38,10 @@ export const NetworkTab = ({
|
||||||
<InfoTooltip label="The hostname or IP address on which KoboldCpp will bind its webserver to." />
|
<InfoTooltip label="The hostname or IP address on which KoboldCpp will bind its webserver to." />
|
||||||
</Group>
|
</Group>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="localhost"
|
label="Host"
|
||||||
|
description="The IP address to bind to"
|
||||||
value={host}
|
value={host}
|
||||||
onChange={(event) => onHostChange(event.currentTarget.value)}
|
onChange={(event) => handleHostChange(event.currentTarget.value)}
|
||||||
style={{ maxWidth: 200 }}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -80,13 +65,13 @@ export const NetworkTab = ({
|
||||||
|
|
||||||
const numValue = Number(value);
|
const numValue = Number(value);
|
||||||
if (!isNaN(numValue) && numValue >= 1 && numValue <= 65535) {
|
if (!isNaN(numValue) && numValue >= 1 && numValue <= 65535) {
|
||||||
onPortChange(numValue);
|
handlePortChange(numValue);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onBlur={(event) => {
|
onBlur={(event) => {
|
||||||
const value = event.currentTarget.value;
|
const value = event.currentTarget.value;
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
onPortChange(undefined);
|
handlePortChange(undefined);
|
||||||
setPortInput('');
|
setPortInput('');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
@ -106,7 +91,7 @@ export const NetworkTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={multiuser}
|
checked={multiuser}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onMultiuserChange(event.currentTarget.checked)
|
handleMultiuserChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Multiuser Mode"
|
label="Multiuser Mode"
|
||||||
/>
|
/>
|
||||||
|
|
@ -119,7 +104,7 @@ export const NetworkTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={multiplayer}
|
checked={multiplayer}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onMultiplayerChange(event.currentTarget.checked)
|
handleMultiplayerChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Shared Multiplayer"
|
label="Shared Multiplayer"
|
||||||
/>
|
/>
|
||||||
|
|
@ -134,7 +119,7 @@ export const NetworkTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={remotetunnel}
|
checked={remotetunnel}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onRemotetunnelChange(event.currentTarget.checked)
|
handleRemotetunnelChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Remote Tunnel"
|
label="Remote Tunnel"
|
||||||
/>
|
/>
|
||||||
|
|
@ -147,7 +132,7 @@ export const NetworkTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={nocertify}
|
checked={nocertify}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onNocertifyChange(event.currentTarget.checked)
|
handleNocertifyChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="No Certify Mode (Insecure)"
|
label="No Certify Mode (Insecure)"
|
||||||
/>
|
/>
|
||||||
|
|
@ -160,7 +145,7 @@ export const NetworkTab = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={websearch}
|
checked={websearch}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
onWebsearchChange(event.currentTarget.checked)
|
handleWebsearchChange(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
label="Enable WebSearch"
|
label="Enable WebSearch"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import { Card, Container, Stack, Tabs, Group, Button } from '@mantine/core';
|
import { Card, Container, Stack, Tabs, Group, Button } from '@mantine/core';
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
import { useTrackedConfigHandlers } from '@/hooks/useTrackedConfigHandlers';
|
|
||||||
import { useLaunchLogic } from '@/hooks/useLaunchLogic';
|
import { useLaunchLogic } from '@/hooks/useLaunchLogic';
|
||||||
import { useWarnings } from '@/hooks/useWarnings';
|
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 { AdvancedTab } from '@/components/screens/Launch/AdvancedTab';
|
||||||
import { NetworkTab } from '@/components/screens/Launch/NetworkTab';
|
import { NetworkTab } from '@/components/screens/Launch/NetworkTab';
|
||||||
import { ImageGenerationTab } from '@/components/screens/Launch/ImageGenerationTab';
|
import { ImageGenerationTab } from '@/components/screens/Launch/ImageGenerationTab';
|
||||||
|
|
@ -25,7 +24,6 @@ export const LaunchScreen = ({
|
||||||
const [selectedFile, setSelectedFile] = useState<string | null>(null);
|
const [selectedFile, setSelectedFile] = useState<string | null>(null);
|
||||||
const [, setInstallDir] = useState<string>('');
|
const [, setInstallDir] = useState<string>('');
|
||||||
const [activeTab, setActiveTab] = useState<string | null>('general');
|
const [activeTab, setActiveTab] = useState<string | null>('general');
|
||||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
||||||
const [warnings, setWarnings] = useState<
|
const [warnings, setWarnings] = useState<
|
||||||
Array<{ type: 'warning' | 'info'; message: string }>
|
Array<{ type: 'warning' | 'info'; message: string }>
|
||||||
>([]);
|
>([]);
|
||||||
|
|
@ -51,6 +49,7 @@ export const LaunchScreen = ({
|
||||||
quantmatmul,
|
quantmatmul,
|
||||||
backend,
|
backend,
|
||||||
gpuDevice,
|
gpuDevice,
|
||||||
|
gpuPlatform,
|
||||||
sdmodel,
|
sdmodel,
|
||||||
sdt5xxl,
|
sdt5xxl,
|
||||||
sdclipl,
|
sdclipl,
|
||||||
|
|
@ -59,78 +58,9 @@ export const LaunchScreen = ({
|
||||||
sdvae,
|
sdvae,
|
||||||
sdlora,
|
sdlora,
|
||||||
parseAndApplyConfigFile,
|
parseAndApplyConfigFile,
|
||||||
loadSavedSettings,
|
|
||||||
loadConfigFromFile,
|
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();
|
} = 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({
|
const { isLaunching, handleLaunch } = useLaunchLogic({
|
||||||
modelPath,
|
modelPath,
|
||||||
sdmodel,
|
sdmodel,
|
||||||
|
|
@ -156,13 +86,11 @@ export const LaunchScreen = ({
|
||||||
setSelectedFile(files[0].name);
|
setSelectedFile(files[0].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadSavedSettings();
|
const loadedConfigFileName = await loadConfigFromFile(files, savedConfig);
|
||||||
|
if (loadedConfigFileName && !selectedFile) {
|
||||||
const currentSelectedFile = await loadConfigFromFile(files, savedConfig);
|
setSelectedFile(loadedConfigFileName);
|
||||||
if (currentSelectedFile && !selectedFile) {
|
|
||||||
setSelectedFile(currentSelectedFile);
|
|
||||||
}
|
}
|
||||||
}, [selectedFile, loadSavedSettings, loadConfigFromFile]);
|
}, [selectedFile, loadConfigFromFile]);
|
||||||
|
|
||||||
const handleFileSelection = async (fileName: string) => {
|
const handleFileSelection = async (fileName: string) => {
|
||||||
setSelectedFile(fileName);
|
setSelectedFile(fileName);
|
||||||
|
|
@ -172,14 +100,19 @@ export const LaunchScreen = ({
|
||||||
if (selectedConfig) {
|
if (selectedConfig) {
|
||||||
await parseAndApplyConfigFile(selectedConfig.path);
|
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,
|
gpulayers: gpuLayers,
|
||||||
contextsize: contextSize,
|
contextsize: contextSize,
|
||||||
model_param: modelPath,
|
model: modelPath,
|
||||||
port,
|
port,
|
||||||
host,
|
host,
|
||||||
multiuser: multiuser ? 1 : 0,
|
multiuser: multiuser ? 1 : 0,
|
||||||
|
|
@ -193,14 +126,15 @@ export const LaunchScreen = ({
|
||||||
failsafe,
|
failsafe,
|
||||||
usecuda: backend === 'cuda' || backend === 'rocm',
|
usecuda: backend === 'cuda' || backend === 'rocm',
|
||||||
usevulkan: backend === 'vulkan',
|
usevulkan: backend === 'vulkan',
|
||||||
useclblast: backend === 'clblast',
|
useclblast: useclblastValue,
|
||||||
sdmodel,
|
sdmodel,
|
||||||
sdt5xxl,
|
sdt5xxl,
|
||||||
sdclipl,
|
sdclipl,
|
||||||
sdclipg,
|
sdclipg,
|
||||||
sdphotomaker,
|
sdphotomaker,
|
||||||
sdvae,
|
sdvae,
|
||||||
});
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const handleCreateNewConfig = async (configName: string) => {
|
const handleCreateNewConfig = async (configName: string) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -214,7 +148,6 @@ export const LaunchScreen = ({
|
||||||
const newFileName = `${configName}.kcpps`;
|
const newFileName = `${configName}.kcpps`;
|
||||||
setSelectedFile(newFileName);
|
setSelectedFile(newFileName);
|
||||||
await window.electronAPI.kobold.setSelectedConfig(newFileName);
|
await window.electronAPI.kobold.setSelectedConfig(newFileName);
|
||||||
setHasUnsavedChanges(false);
|
|
||||||
} else {
|
} else {
|
||||||
window.electronAPI.logs.logError(
|
window.electronAPI.logs.logError(
|
||||||
'Failed to create new configuration',
|
'Failed to create new configuration',
|
||||||
|
|
@ -246,9 +179,7 @@ export const LaunchScreen = ({
|
||||||
buildConfigData()
|
buildConfigData()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (success) {
|
if (!success) {
|
||||||
setHasUnsavedChanges(false);
|
|
||||||
} else {
|
|
||||||
window.electronAPI.logs.logError(
|
window.electronAPI.logs.logError(
|
||||||
'Failed to save configuration',
|
'Failed to save configuration',
|
||||||
new Error('Save operation failed')
|
new Error('Save operation failed')
|
||||||
|
|
@ -318,7 +249,6 @@ export const LaunchScreen = ({
|
||||||
<ConfigFileManager
|
<ConfigFileManager
|
||||||
configFiles={configFiles}
|
configFiles={configFiles}
|
||||||
selectedFile={selectedFile}
|
selectedFile={selectedFile}
|
||||||
hasUnsavedChanges={hasUnsavedChanges}
|
|
||||||
onFileSelection={handleFileSelection}
|
onFileSelection={handleFileSelection}
|
||||||
onCreateNewConfig={handleCreateNewConfig}
|
onCreateNewConfig={handleCreateNewConfig}
|
||||||
onSaveConfig={handleSaveConfig}
|
onSaveConfig={handleSaveConfig}
|
||||||
|
|
@ -335,138 +265,19 @@ export const LaunchScreen = ({
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Tabs.Panel value="general" pt="md">
|
<Tabs.Panel value="general" pt="md">
|
||||||
<GeneralTab
|
<GeneralTab onWarningsChange={setWarnings} />
|
||||||
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}
|
|
||||||
/>
|
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|
||||||
<Tabs.Panel value="advanced" pt="md">
|
<Tabs.Panel value="advanced" pt="md">
|
||||||
<AdvancedTab
|
<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
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|
||||||
<Tabs.Panel value="network" pt="md">
|
<Tabs.Panel value="network" pt="md">
|
||||||
<NetworkTab
|
<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
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|
||||||
<Tabs.Panel value="image" pt="md">
|
<Tabs.Panel value="image" pt="md">
|
||||||
<ImageGenerationTab
|
<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}
|
|
||||||
/>
|
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
</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 { useLaunchConfigStore } from '@/stores/launchConfigStore';
|
||||||
import type { ConfigFile } from '@/types';
|
|
||||||
import {
|
import {
|
||||||
getPresetByName,
|
IMAGE_MODEL_PRESETS,
|
||||||
type ImageModelPreset,
|
type ImageModelPreset,
|
||||||
} from '@/utils/imageModelPresets';
|
} from '@/utils/imageModelPresets';
|
||||||
import {
|
|
||||||
DEFAULT_CONTEXT_SIZE,
|
|
||||||
DEFAULT_MODEL_URL,
|
|
||||||
DEFAULT_HOST,
|
|
||||||
} from '@/constants';
|
|
||||||
|
|
||||||
export const useLaunchConfig = () => {
|
export const useLaunchConfig = () => {
|
||||||
const [gpuLayers, setGpuLayers] = useState<number>(0);
|
const state = useLaunchConfigStore();
|
||||||
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]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gpuLayers,
|
gpuLayers: state.gpuLayers,
|
||||||
autoGpuLayers,
|
autoGpuLayers: state.autoGpuLayers,
|
||||||
contextSize,
|
contextSize: state.contextSize,
|
||||||
modelPath,
|
modelPath: state.modelPath,
|
||||||
additionalArguments,
|
additionalArguments: state.additionalArguments,
|
||||||
port,
|
port: state.port,
|
||||||
host,
|
host: state.host,
|
||||||
multiuser,
|
multiuser: state.multiuser,
|
||||||
multiplayer,
|
multiplayer: state.multiplayer,
|
||||||
remotetunnel,
|
remotetunnel: state.remotetunnel,
|
||||||
nocertify,
|
nocertify: state.nocertify,
|
||||||
websearch,
|
websearch: state.websearch,
|
||||||
noshift,
|
noshift: state.noshift,
|
||||||
flashattention,
|
flashattention: state.flashattention,
|
||||||
noavx2,
|
noavx2: state.noavx2,
|
||||||
failsafe,
|
failsafe: state.failsafe,
|
||||||
lowvram,
|
lowvram: state.lowvram,
|
||||||
quantmatmul,
|
quantmatmul: state.quantmatmul,
|
||||||
backend,
|
backend: state.backend,
|
||||||
gpuDevice,
|
gpuDevice: state.gpuDevice,
|
||||||
sdmodel,
|
gpuPlatform: state.gpuPlatform,
|
||||||
sdt5xxl,
|
sdmodel: state.sdmodel,
|
||||||
sdclipl,
|
sdt5xxl: state.sdt5xxl,
|
||||||
sdclipg,
|
sdclipl: state.sdclipl,
|
||||||
sdphotomaker,
|
sdclipg: state.sdclipg,
|
||||||
sdvae,
|
sdphotomaker: state.sdphotomaker,
|
||||||
sdlora,
|
sdvae: state.sdvae,
|
||||||
|
sdlora: state.sdlora,
|
||||||
|
|
||||||
parseAndApplyConfigFile,
|
handleGpuLayersChange: state.setGpuLayers,
|
||||||
loadSavedSettings,
|
handleAutoGpuLayersChange: state.setAutoGpuLayers,
|
||||||
loadConfigFromFile,
|
handleContextSizeChangeWithStep: state.contextSizeChangeWithStep,
|
||||||
handleGpuLayersChange,
|
handleModelPathChange: state.setModelPath,
|
||||||
handleAutoGpuLayersChange,
|
handleAdditionalArgumentsChange: state.setAdditionalArguments,
|
||||||
handleContextSizeChangeWithStep,
|
handlePortChange: state.setPort,
|
||||||
handleModelPathChange,
|
handleHostChange: state.setHost,
|
||||||
handleSelectModelFile,
|
handleMultiuserChange: state.setMultiuser,
|
||||||
handleAdditionalArgumentsChange,
|
handleMultiplayerChange: state.setMultiplayer,
|
||||||
handlePortChange,
|
handleRemotetunnelChange: state.setRemotetunnel,
|
||||||
handleHostChange,
|
handleNocertifyChange: state.setNocertify,
|
||||||
handleMultiuserChange,
|
handleWebsearchChange: state.setWebsearch,
|
||||||
handleMultiplayerChange,
|
handleNoshiftChange: state.setNoshift,
|
||||||
handleRemotetunnelChange,
|
handleFlashattentionChange: state.setFlashattention,
|
||||||
handleNocertifyChange,
|
handleNoavx2Change: state.setNoavx2,
|
||||||
handleWebsearchChange,
|
handleFailsafeChange: state.setFailsafe,
|
||||||
handleNoshiftChange,
|
handleLowvramChange: state.setLowvram,
|
||||||
handleFlashattentionChange,
|
handleQuantmatmulChange: state.setQuantmatmul,
|
||||||
handleNoavx2Change,
|
handleBackendChange: state.setBackend,
|
||||||
handleFailsafeChange,
|
handleGpuDeviceChange: state.setGpuDevice,
|
||||||
handleLowvramChange,
|
handleGpuPlatformChange: state.setGpuPlatform,
|
||||||
handleQuantmatmulChange,
|
handleSdmodelChange: state.setSdmodel,
|
||||||
handleBackendChange,
|
handleSdt5xxlChange: state.setSdt5xxl,
|
||||||
handleGpuDeviceChange,
|
handleSdcliplChange: state.setSdclipl,
|
||||||
handleSdmodelChange,
|
handleSdclipgChange: state.setSdclipg,
|
||||||
handleSelectSdmodelFile,
|
handleSdphotomakerChange: state.setSdphotomaker,
|
||||||
handleSdt5xxlChange,
|
handleSdvaeChange: state.setSdvae,
|
||||||
handleSelectSdt5xxlFile,
|
handleSdloraChange: state.setSdlora,
|
||||||
handleSdcliplChange,
|
|
||||||
handleSelectSdcliplFile,
|
parseAndApplyConfigFile: state.parseAndApplyConfigFile,
|
||||||
handleSdclipgChange,
|
loadConfigFromFile: state.loadConfigFromFile,
|
||||||
handleSelectSdclipgFile,
|
handleSelectModelFile: state.selectModelFile,
|
||||||
handleSdphotomakerChange,
|
handleImageModelPresetChange: state.applyImageModelPreset,
|
||||||
handleSelectSdphotomakerFile,
|
handleApplyPreset: (presetName: string) => {
|
||||||
handleSdvaeChange,
|
const preset = IMAGE_MODEL_PRESETS.find(
|
||||||
handleSelectSdvaeFile,
|
(p: ImageModelPreset) => p.name === presetName
|
||||||
handleSdloraChange,
|
);
|
||||||
handleSelectSdloraFile,
|
if (preset) {
|
||||||
applyImageModelPreset,
|
state.applyImageModelPreset(preset);
|
||||||
handleApplyPreset,
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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 { useState, useCallback } from 'react';
|
||||||
|
import { parseCLBlastDevice } from '@/utils';
|
||||||
|
|
||||||
interface UseLaunchLogicProps {
|
interface UseLaunchLogicProps {
|
||||||
modelPath: string;
|
modelPath: string;
|
||||||
|
|
@ -22,7 +23,7 @@ interface LaunchArgs {
|
||||||
flashattention: boolean;
|
flashattention: boolean;
|
||||||
backend: string;
|
backend: string;
|
||||||
lowvram: boolean;
|
lowvram: boolean;
|
||||||
gpuDevice: number;
|
gpuDevice: number | string;
|
||||||
quantmatmul: boolean;
|
quantmatmul: boolean;
|
||||||
additionalArguments: string;
|
additionalArguments: string;
|
||||||
sdt5xxl: string;
|
sdt5xxl: string;
|
||||||
|
|
@ -116,13 +117,33 @@ const buildBackendArgs = (launchArgs: LaunchArgs): string[] => {
|
||||||
if (launchArgs.backend === 'cuda' || launchArgs.backend === 'rocm') {
|
if (launchArgs.backend === 'cuda' || launchArgs.backend === 'rocm') {
|
||||||
const cudaArgs = ['--usecuda'];
|
const cudaArgs = ['--usecuda'];
|
||||||
cudaArgs.push(launchArgs.lowvram ? 'lowvram' : 'normal');
|
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');
|
cudaArgs.push(launchArgs.quantmatmul ? 'mmq' : 'nommq');
|
||||||
args.push(...cudaArgs);
|
args.push(...cudaArgs);
|
||||||
} else if (launchArgs.backend === 'vulkan') {
|
} else if (launchArgs.backend === 'vulkan') {
|
||||||
args.push('--usevulkan');
|
args.push('--usevulkan');
|
||||||
} else if (launchArgs.backend === 'clblast') {
|
} 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;
|
handleLowvramChange: (enabled: boolean) => void;
|
||||||
handleQuantmatmulChange: (enabled: boolean) => void;
|
handleQuantmatmulChange: (enabled: boolean) => void;
|
||||||
handleBackendChange: (backend: string) => void;
|
handleBackendChange: (backend: string) => void;
|
||||||
|
handleGpuDeviceChange: (device: number) => void;
|
||||||
|
handleGpuPlatformChange: (platform: number) => void;
|
||||||
handleSdmodelChange: (path: string) => void;
|
handleSdmodelChange: (path: string) => void;
|
||||||
handleSdt5xxlChange: (path: string) => void;
|
handleSdt5xxlChange: (path: string) => void;
|
||||||
handleSdcliplChange: (path: string) => void;
|
handleSdcliplChange: (path: string) => void;
|
||||||
|
|
@ -58,6 +60,8 @@ export const useTrackedConfigHandlers = ({
|
||||||
handleLowvramChange,
|
handleLowvramChange,
|
||||||
handleQuantmatmulChange,
|
handleQuantmatmulChange,
|
||||||
handleBackendChange,
|
handleBackendChange,
|
||||||
|
handleGpuDeviceChange,
|
||||||
|
handleGpuPlatformChange,
|
||||||
handleSdmodelChange,
|
handleSdmodelChange,
|
||||||
handleSdt5xxlChange,
|
handleSdt5xxlChange,
|
||||||
handleSdcliplChange,
|
handleSdcliplChange,
|
||||||
|
|
@ -144,6 +148,14 @@ export const useTrackedConfigHandlers = ({
|
||||||
handleBackendChange,
|
handleBackendChange,
|
||||||
setHasUnsavedChanges
|
setHasUnsavedChanges
|
||||||
),
|
),
|
||||||
|
handleGpuDeviceChangeWithTracking: createChangeTracker(
|
||||||
|
handleGpuDeviceChange,
|
||||||
|
setHasUnsavedChanges
|
||||||
|
),
|
||||||
|
handleGpuPlatformChangeWithTracking: createChangeTracker(
|
||||||
|
handleGpuPlatformChange,
|
||||||
|
setHasUnsavedChanges
|
||||||
|
),
|
||||||
handleSdmodelChangeWithTracking: createChangeTracker(
|
handleSdmodelChangeWithTracking: createChangeTracker(
|
||||||
handleSdmodelChange,
|
handleSdmodelChange,
|
||||||
setHasUnsavedChanges
|
setHasUnsavedChanges
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,7 @@ export class KoboldCppManager {
|
||||||
async parseConfigFile(filePath: string): Promise<{
|
async parseConfigFile(filePath: string): Promise<{
|
||||||
gpulayers?: number;
|
gpulayers?: number;
|
||||||
contextsize?: number;
|
contextsize?: number;
|
||||||
model_param?: string;
|
model?: string;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
} | null> {
|
} | null> {
|
||||||
try {
|
try {
|
||||||
|
|
@ -371,7 +371,7 @@ export class KoboldCppManager {
|
||||||
configData: {
|
configData: {
|
||||||
gpulayers?: number;
|
gpulayers?: number;
|
||||||
contextsize?: number;
|
contextsize?: number;
|
||||||
model_param?: string;
|
model?: string;
|
||||||
port?: number;
|
port?: number;
|
||||||
host?: string;
|
host?: string;
|
||||||
multiuser?: number;
|
multiuser?: number;
|
||||||
|
|
@ -385,7 +385,7 @@ export class KoboldCppManager {
|
||||||
failsafe?: boolean;
|
failsafe?: boolean;
|
||||||
usecuda?: boolean;
|
usecuda?: boolean;
|
||||||
usevulkan?: boolean;
|
usevulkan?: boolean;
|
||||||
useclblast?: boolean;
|
useclblast?: [number, number] | boolean;
|
||||||
sdmodel?: string;
|
sdmodel?: string;
|
||||||
sdt5xxl?: string;
|
sdt5xxl?: string;
|
||||||
sdclipl?: string;
|
sdclipl?: string;
|
||||||
|
|
@ -615,6 +615,7 @@ export class KoboldCppManager {
|
||||||
|
|
||||||
this.koboldProcess = spawn(versionPath, args, {
|
this.koboldProcess = spawn(versionPath, args, {
|
||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
|
shell: process.platform === 'win32',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (onOutput) {
|
if (onOutput) {
|
||||||
|
|
@ -938,6 +939,7 @@ export class KoboldCppManager {
|
||||||
const child = spawn(currentVersion.path, finalArgs, {
|
const child = spawn(currentVersion.path, finalArgs, {
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
detached: false,
|
detached: false,
|
||||||
|
shell: process.platform === 'win32',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.koboldProcess = child;
|
this.koboldProcess = child;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@ export interface BackendSupport {
|
||||||
|
|
||||||
export class BinaryService {
|
export class BinaryService {
|
||||||
private backendSupportCache = new Map<string, BackendSupport>();
|
private backendSupportCache = new Map<string, BackendSupport>();
|
||||||
|
private availableBackendsCache = new Map<
|
||||||
|
string,
|
||||||
|
Array<{ value: string; label: string; devices?: string[] }>
|
||||||
|
>();
|
||||||
private logManager: LogManager;
|
private logManager: LogManager;
|
||||||
|
|
||||||
constructor(logManager: LogManager) {
|
constructor(logManager: LogManager) {
|
||||||
|
|
@ -78,6 +82,12 @@ export class BinaryService {
|
||||||
clblast: { supported: boolean; devices: string[] };
|
clblast: { supported: boolean; devices: string[] };
|
||||||
}
|
}
|
||||||
): Array<{ value: string; label: string; 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 backendSupport = this.detectBackendSupport(koboldBinaryPath);
|
||||||
const backends: Array<{
|
const backends: Array<{
|
||||||
value: string;
|
value: string;
|
||||||
|
|
@ -122,10 +132,12 @@ export class BinaryService {
|
||||||
label: 'CPU',
|
label: 'CPU',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.availableBackendsCache.set(cacheKey, backends);
|
||||||
return backends;
|
return backends;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache(): void {
|
clearCache(): void {
|
||||||
this.backendSupportCache.clear();
|
this.backendSupportCache.clear();
|
||||||
|
this.availableBackendsCache.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/* eslint-disable no-comments/disallowComments */
|
/* eslint-disable no-comments/disallowComments */
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
|
import * as openclInfo from 'opencl-info';
|
||||||
import { shortenDeviceName } from '@/utils';
|
import { shortenDeviceName } from '@/utils';
|
||||||
import type {
|
import type {
|
||||||
CPUCapabilities,
|
CPUCapabilities,
|
||||||
|
|
@ -343,74 +344,35 @@ export class HardwareService {
|
||||||
devices: string[];
|
devices: string[];
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const { spawn } = await import('child_process');
|
const platforms = openclInfo.getPlatformInfo();
|
||||||
const clinfo = spawn('clinfo', ['--json'], { timeout: 5000 });
|
|
||||||
|
|
||||||
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[] = [];
|
const devices: string[] = [];
|
||||||
|
|
||||||
if (data.platforms) {
|
for (
|
||||||
for (const platform of data.platforms) {
|
let platformIndex = 0;
|
||||||
|
platformIndex < platforms.length;
|
||||||
|
platformIndex++
|
||||||
|
) {
|
||||||
|
const platform = platforms[platformIndex];
|
||||||
if (platform.devices) {
|
if (platform.devices) {
|
||||||
for (const device of platform.devices) {
|
for (
|
||||||
if (device.name && device.type !== 'CPU') {
|
let deviceIndex = 0;
|
||||||
devices.push(shortenDeviceName(device.name));
|
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,
|
supported: devices.length > 0,
|
||||||
devices,
|
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 {
|
} catch {
|
||||||
return { supported: false, devices: [] };
|
return { supported: false, devices: [] };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ const koboldAPI: KoboldAPI = {
|
||||||
configData: {
|
configData: {
|
||||||
gpulayers?: number;
|
gpulayers?: number;
|
||||||
contextsize?: number;
|
contextsize?: number;
|
||||||
model_param?: string;
|
model?: string;
|
||||||
port?: number;
|
port?: number;
|
||||||
host?: string;
|
host?: string;
|
||||||
multiuser?: number;
|
multiuser?: number;
|
||||||
|
|
@ -69,7 +69,7 @@ const koboldAPI: KoboldAPI = {
|
||||||
failsafe?: boolean;
|
failsafe?: boolean;
|
||||||
usecuda?: boolean;
|
usecuda?: boolean;
|
||||||
usevulkan?: boolean;
|
usevulkan?: boolean;
|
||||||
useclblast?: boolean;
|
useclblast?: [number, number] | boolean;
|
||||||
sdmodel?: string;
|
sdmodel?: string;
|
||||||
sdt5xxl?: string;
|
sdt5xxl?: string;
|
||||||
sdclipl?: 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: {
|
configData: {
|
||||||
gpulayers?: number;
|
gpulayers?: number;
|
||||||
contextsize?: number;
|
contextsize?: number;
|
||||||
model_param?: string;
|
model?: string;
|
||||||
port?: number;
|
port?: number;
|
||||||
host?: string;
|
host?: string;
|
||||||
multiuser?: number;
|
multiuser?: number;
|
||||||
|
|
@ -124,7 +124,7 @@ export interface KoboldAPI {
|
||||||
failsafe?: boolean;
|
failsafe?: boolean;
|
||||||
usecuda?: boolean;
|
usecuda?: boolean;
|
||||||
usevulkan?: boolean;
|
usevulkan?: boolean;
|
||||||
useclblast?: boolean;
|
useclblast?: [number, number] | boolean;
|
||||||
sdmodel?: string;
|
sdmodel?: string;
|
||||||
sdt5xxl?: string;
|
sdt5xxl?: string;
|
||||||
sdclipl?: string;
|
sdclipl?: string;
|
||||||
|
|
@ -139,7 +139,7 @@ export interface KoboldAPI {
|
||||||
parseConfigFile: (filePath: string) => Promise<{
|
parseConfigFile: (filePath: string) => Promise<{
|
||||||
gpulayers?: number;
|
gpulayers?: number;
|
||||||
contextsize?: number;
|
contextsize?: number;
|
||||||
model_param?: string;
|
model?: string;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
} | null>;
|
} | null>;
|
||||||
selectModelFile: () => Promise<string | 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 './assets';
|
||||||
|
export * from './clblast';
|
||||||
export * from './downloadUtils';
|
export * from './downloadUtils';
|
||||||
export * from './fileSize';
|
export * from './fileSize';
|
||||||
export * from './hardware';
|
export * from './hardware';
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue