import { create } from 'zustand'; import type { ConfigFile, SdConvDirectMode } from '@/types'; import type { ImageModelPreset } from '@/constants/imageModelPresets'; import { DEFAULT_AUTO_GPU_LAYERS, DEFAULT_CONTEXT_SIZE } from '@/constants'; interface LaunchConfigState { gpuLayers: number; autoGpuLayers: boolean; contextSize: number; model: 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; usemmap: boolean; backend: string; gpuDeviceSelection: string; tensorSplit: string; gpuPlatform: number; sdmodel: string; sdt5xxl: string; sdclipl: string; sdclipg: string; sdphotomaker: string; sdvae: string; sdlora: string; sdconvdirect: SdConvDirectMode; sdvaecpu: boolean; sdclipgpu: boolean; moecpu: number; moeexperts: number; isImageGenerationMode: boolean; isTextMode: boolean; setGpuLayers: (layers: number) => void; setAutoGpuLayers: (auto: boolean) => void; setContextSize: (size: number) => void; setModel: (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; setUsemmap: (usemmap: boolean) => void; setBackend: (backend: string) => void; setGpuDeviceSelection: (selection: string) => void; setTensorSplit: (split: string) => 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; setSdconvdirect: (mode: SdConvDirectMode) => void; setSdvaecpu: (enabled: boolean) => void; setSdclipgpu: (enabled: boolean) => void; setMoecpu: (moecpu: number) => void; setMoeexperts: (moeexperts: number) => void; parseAndApplyConfigFile: (configPath: string) => Promise; loadConfigFromFile: ( configFiles: ConfigFile[], savedConfig: string | null ) => Promise; selectModelFile: () => Promise; selectSdmodelFile: () => Promise; selectSdt5xxlFile: () => Promise; selectSdcliplFile: () => Promise; selectSdclipgFile: () => Promise; selectSdphotomakerFile: () => Promise; selectSdvaeFile: () => Promise; selectSdloraFile: () => Promise; contextSizeChangeWithStep: (size: number) => void; applyImageModelPreset: (preset: ImageModelPreset) => void; } export const useLaunchConfigStore = create((set, get) => ({ gpuLayers: 0, autoGpuLayers: DEFAULT_AUTO_GPU_LAYERS, contextSize: DEFAULT_CONTEXT_SIZE, model: '', additionalArguments: '', port: undefined, host: '', multiuser: false, multiplayer: false, remotetunnel: false, nocertify: false, websearch: false, noshift: false, flashattention: true, noavx2: false, failsafe: false, lowvram: false, quantmatmul: true, usemmap: true, backend: '', gpuDeviceSelection: '0', tensorSplit: '', gpuPlatform: 0, sdmodel: '', sdt5xxl: '', sdclipl: '', sdclipg: '', sdphotomaker: '', sdvae: '', sdlora: '', sdconvdirect: 'off' as const, sdvaecpu: false, sdclipgpu: false, moecpu: 0, moeexperts: -1, isImageGenerationMode: false, isTextMode: false, setGpuLayers: (layers) => set({ gpuLayers: layers }), setAutoGpuLayers: (auto) => set({ autoGpuLayers: auto }), setContextSize: (size) => set({ contextSize: size }), setModel: (path) => set({ model: path, isTextMode: Boolean(path?.trim()), }), 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 }), setUsemmap: (usemmap) => set({ usemmap }), setBackend: (backend) => set({ backend, gpuDeviceSelection: '0', tensorSplit: '', }), setGpuDeviceSelection: (selection) => set({ gpuDeviceSelection: selection }), setTensorSplit: (split) => set({ tensorSplit: split }), setGpuPlatform: (platform) => set({ gpuPlatform: platform }), setSdmodel: (model) => set({ sdmodel: model, isImageGenerationMode: Boolean(model?.trim()), }), 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 }), setSdconvdirect: (mode) => set({ sdconvdirect: mode }), setSdvaecpu: (enabled) => set({ sdvaecpu: enabled }), setSdclipgpu: (enabled) => set({ sdclipgpu: enabled }), setMoecpu: (moeCpu) => set({ moecpu: moeCpu }), setMoeexperts: (moeExperts) => set({ moeexperts: moeExperts }), // eslint-disable-next-line sonarjs/cognitive-complexity parseAndApplyConfigFile: async (configPath: string) => { const configData = await window.electronAPI.kobold.parseConfigFile(configPath); if (configData) { const updates: Partial = {}; if (typeof configData.autoGpuLayers === 'boolean') { updates.autoGpuLayers = configData.autoGpuLayers; } else { updates.autoGpuLayers = DEFAULT_AUTO_GPU_LAYERS; } 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.model = configData.model; updates.isTextMode = Boolean(configData.model?.trim()); } if (typeof configData.additionalArguments === 'string') { updates.additionalArguments = configData.additionalArguments; } else { updates.additionalArguments = ''; } 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 = ''; } 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 (typeof configData.usemmap === 'boolean') { updates.usemmap = configData.usemmap; } else { updates.usemmap = true; } 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.gpuDeviceSelection = deviceId || '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.gpuDeviceSelection = deviceIndex.toString(); updates.gpuPlatform = platformIndex; } else { updates.backend = 'cpu'; } if (typeof configData.gpuDeviceSelection === 'string') { updates.gpuDeviceSelection = configData.gpuDeviceSelection; } if (typeof configData.tensorSplit === 'string') { updates.tensorSplit = configData.tensorSplit; } if (typeof configData.sdmodel === 'string') { updates.sdmodel = configData.sdmodel; updates.isImageGenerationMode = Boolean(configData.sdmodel?.trim()); } 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; } if ( typeof configData.sdconvdirect === 'string' && ['off', 'vaeonly', 'full'].includes(configData.sdconvdirect) ) { updates.sdconvdirect = configData.sdconvdirect as SdConvDirectMode; } if (typeof configData.sdvaecpu === 'boolean') { updates.sdvaecpu = configData.sdvaecpu; } else { updates.sdvaecpu = false; } if (typeof configData.sdclipgpu === 'boolean') { updates.sdclipgpu = configData.sdclipgpu; } else { updates.sdclipgpu = false; } if (typeof configData.moecpu === 'number') { updates.moecpu = configData.moecpu; } else { updates.moecpu = 0; } if (typeof configData.moeexperts === 'number') { updates.moeexperts = configData.moeexperts; } else { updates.moeexperts = -1; } set(updates); } }, loadConfigFromFile: async ( configFiles: ConfigFile[], savedConfig: 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( 'Select a Text Model File' ); if (result) { set({ model: result, isTextMode: Boolean(result?.trim()), }); } }, selectSdmodelFile: async () => { const result = await window.electronAPI.kobold.selectModelFile( 'Select a Image Gen. Model File' ); if (result) { set({ sdmodel: result, isImageGenerationMode: Boolean(result?.trim()), }); } }, selectSdt5xxlFile: async () => { const result = await window.electronAPI.kobold.selectModelFile( 'Select a T5XXL Model File' ); if (result) { set({ sdt5xxl: result }); } }, selectSdcliplFile: async () => { const result = await window.electronAPI.kobold.selectModelFile( 'Select a CLIP-L Model File' ); if (result) { set({ sdclipl: result }); } }, selectSdclipgFile: async () => { const result = await window.electronAPI.kobold.selectModelFile( 'Select a CLIP-G Model File' ); if (result) { set({ sdclipg: result }); } }, selectSdphotomakerFile: async () => { const result = await window.electronAPI.kobold.selectModelFile( 'Select a PhotoMaker Model File' ); if (result) { set({ sdphotomaker: result }); } }, selectSdvaeFile: async () => { const result = await window.electronAPI.kobold.selectModelFile( 'Select a VAE Model File' ); if (result) { set({ sdvae: result }); } }, selectSdloraFile: async () => { const result = await window.electronAPI.kobold.selectModelFile( 'Select a LORA Model File' ); 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({ sdmodel: preset.sdmodel, isImageGenerationMode: Boolean(preset.sdmodel?.trim()), sdt5xxl: preset.sdt5xxl, sdclipl: preset.sdclipl, sdclipg: preset.sdclipg || '', sdvae: preset.sdvae, model: '', }); }, }));