mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
refactor to use a util to try/catch->logError, hover states on badges
This commit is contained in:
parent
2723b79c6e
commit
38c504be8b
23 changed files with 264 additions and 276 deletions
1
.github/copilot-instructions.md
vendored
1
.github/copilot-instructions.md
vendored
|
|
@ -10,6 +10,7 @@
|
|||
- Stop asking me to run the "dev" script to test changes
|
||||
- Try to move helper functions from component code to their own separate files to help minimize clutter
|
||||
- Always use absolute imports (e.g. `import { MyComponent } from '@/components/MyComponent'`)
|
||||
- Never add explicit return types to functions. We want to rely on implicit types as much as possible
|
||||
|
||||
### Logging and Error Handling
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "gerbil",
|
||||
"productName": "Gerbil",
|
||||
"version": "1.4.0-beta.2",
|
||||
"version": "1.4.0-beta.3",
|
||||
"description": "Run Large Language Models locally",
|
||||
"main": "out/main/index.js",
|
||||
"homepage": "./",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { Badge, Tooltip } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
|
||||
interface PerformanceBadgeProps {
|
||||
label: string;
|
||||
|
|
@ -11,20 +13,19 @@ export const PerformanceBadge = ({
|
|||
value,
|
||||
tooltipLabel,
|
||||
}: PerformanceBadgeProps) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handlePerformanceClick = async () => {
|
||||
try {
|
||||
const result = await window.electronAPI.app.openPerformanceManager();
|
||||
if (!result.success) {
|
||||
const result = await safeExecute(
|
||||
() => window.electronAPI.app.openPerformanceManager(),
|
||||
'Failed to open performance manager'
|
||||
);
|
||||
|
||||
if (result && !result.success) {
|
||||
window.electronAPI.logs.logError(
|
||||
`Failed to open performance manager: ${result.error}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
window.electronAPI.logs.logError(
|
||||
'Error opening performance manager',
|
||||
error as Error
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -36,7 +37,13 @@ export const PerformanceBadge = ({
|
|||
minWidth: '5rem',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
transition: 'background-color 0.2s ease',
|
||||
backgroundColor: isHovered
|
||||
? 'var(--mantine-color-blue-1)'
|
||||
: undefined,
|
||||
}}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
onClick={handlePerformanceClick}
|
||||
>
|
||||
{label}: {value}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { logError, safeExecute } from '@/utils/logger';
|
||||
import { safeExecute, tryExecute } from '@/utils/logger';
|
||||
import { compareVersions } from '@/utils/version';
|
||||
import { GITHUB_API } from '@/constants';
|
||||
|
||||
|
|
@ -40,16 +40,15 @@ export const useAppUpdateChecker = () => {
|
|||
if (!canAutoUpdate) return;
|
||||
|
||||
setIsDownloading(true);
|
||||
try {
|
||||
|
||||
await tryExecute(async () => {
|
||||
const success = await window.electronAPI.updater.downloadUpdate();
|
||||
if (success) {
|
||||
setIsUpdateDownloaded(true);
|
||||
}
|
||||
} catch (err) {
|
||||
logError('Failed to download update:', err as Error);
|
||||
} finally {
|
||||
}, 'Failed to download update');
|
||||
|
||||
setIsDownloading(false);
|
||||
}
|
||||
}, [canAutoUpdate]);
|
||||
|
||||
const installUpdate = useCallback(() => {
|
||||
|
|
@ -59,7 +58,7 @@ export const useAppUpdateChecker = () => {
|
|||
}, [isUpdateDownloaded]);
|
||||
|
||||
const checkForAppUpdates = useCallback(async () => {
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const currentVersion = await window.electronAPI.app.getVersion();
|
||||
|
||||
const response = await fetch(
|
||||
|
|
@ -87,9 +86,7 @@ export const useAppUpdateChecker = () => {
|
|||
};
|
||||
|
||||
setUpdateInfo(updateInfo);
|
||||
} catch (err) {
|
||||
logError('Failed to check for app updates:', err as Error);
|
||||
}
|
||||
}, 'Failed to check for app updates');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { logError } from '@/utils/logger';
|
||||
import { logError, tryExecuteImmediate } from '@/utils/logger';
|
||||
import { getROCmDownload } from '@/utils/rocm';
|
||||
import { GITHUB_API } from '@/constants';
|
||||
import { filterAssetsByPlatform } from '@/utils/platform';
|
||||
|
|
@ -41,15 +41,13 @@ const loadFromCache = (): CachedReleaseData | null => {
|
|||
};
|
||||
|
||||
const saveToCache = (releases: DownloadItem[]) => {
|
||||
try {
|
||||
tryExecuteImmediate(() => {
|
||||
const data: CachedReleaseData = {
|
||||
releases,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify(data));
|
||||
} catch (err) {
|
||||
logError('Failed to save releases to cache', err as Error);
|
||||
}
|
||||
}, 'Failed to save releases to cache');
|
||||
};
|
||||
|
||||
const transformReleaseToDownloadItems = (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import { logError } from '@/utils/logger';
|
||||
import { tryExecute } from '@/utils/logger';
|
||||
import type { SdConvDirectMode } from '@/types';
|
||||
|
||||
interface UseLaunchLogicProps {
|
||||
|
|
@ -258,7 +258,7 @@ export const useLaunchLogic = ({
|
|||
|
||||
onLaunch();
|
||||
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const args: string[] = [
|
||||
...buildModelArgs(model, sdmodel, launchArgs),
|
||||
...buildConfigArgs(hasImageModel, launchArgs),
|
||||
|
|
@ -283,11 +283,9 @@ export const useLaunchLogic = ({
|
|||
new Error(errorMessage)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
logError('Error launching:', err as Error);
|
||||
} finally {
|
||||
}, 'Error launching');
|
||||
|
||||
setIsLaunching(false);
|
||||
}
|
||||
},
|
||||
[model, sdmodel, isLaunching, onLaunch]
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { logError } from '@/utils/logger';
|
||||
import { tryExecute, safeExecute } from '@/utils/logger';
|
||||
import {
|
||||
getDisplayNameFromPath,
|
||||
compareVersions,
|
||||
|
|
@ -27,32 +27,30 @@ export const useUpdateChecker = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const loadDismissedUpdates = async () => {
|
||||
try {
|
||||
const dismissed = (await window.electronAPI.config.get(
|
||||
'dismissedUpdates'
|
||||
)) as string[] | undefined;
|
||||
const dismissed = await safeExecute(
|
||||
async () =>
|
||||
(await window.electronAPI.config.get('dismissedUpdates')) as
|
||||
| string[]
|
||||
| undefined,
|
||||
'Failed to load dismissed updates'
|
||||
);
|
||||
|
||||
if (dismissed) {
|
||||
setDismissedUpdates(new Set(dismissed));
|
||||
}
|
||||
setDismissedUpdatesLoaded(true);
|
||||
} catch (err) {
|
||||
logError('Failed to load dismissed updates:', err as Error);
|
||||
setDismissedUpdatesLoaded(true);
|
||||
}
|
||||
};
|
||||
|
||||
loadDismissedUpdates();
|
||||
}, []);
|
||||
|
||||
const saveDismissedUpdates = useCallback(async (updates: Set<string>) => {
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
await window.electronAPI.config.set(
|
||||
'dismissedUpdates',
|
||||
Array.from(updates)
|
||||
);
|
||||
} catch (err) {
|
||||
logError('Failed to save dismissed updates:', err as Error);
|
||||
}
|
||||
}, 'Failed to save dismissed updates');
|
||||
}, []);
|
||||
|
||||
const checkForUpdates = useCallback(async () => {
|
||||
|
|
@ -62,7 +60,7 @@ export const useUpdateChecker = () => {
|
|||
|
||||
setIsChecking(true);
|
||||
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const [currentVersion, rocmDownload] = await Promise.all([
|
||||
window.electronAPI.kobold.getCurrentVersion(),
|
||||
getROCmDownload(),
|
||||
|
|
@ -105,11 +103,9 @@ export const useUpdateChecker = () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logError('Failed to check for updates:', err as Error);
|
||||
} finally {
|
||||
}, 'Failed to check for updates');
|
||||
|
||||
setIsChecking(false);
|
||||
}
|
||||
}, [dismissedUpdates, dismissedUpdatesLoaded, releases]);
|
||||
|
||||
const dismissUpdate = useCallback(async () => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
initialize as initializeConfig,
|
||||
getInstallDir,
|
||||
} from '@/main/modules/config';
|
||||
import { logError } from '@/main/modules/logging';
|
||||
import { safeExecute } from '@/utils/node/logger';
|
||||
import { cleanup } from '@/main/modules/koboldcpp';
|
||||
import { getSillyTavernManager } from '@/main/modules/sillytavern';
|
||||
import { cleanup as cleanupOpenWebUI } from '@/main/modules/openwebui';
|
||||
|
|
@ -38,7 +38,7 @@ export async function initializeApp() {
|
|||
app.on('before-quit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
await safeExecute(async () => {
|
||||
const cleanupPromises = [
|
||||
cleanup(),
|
||||
getSillyTavernManager().cleanup(),
|
||||
|
|
@ -53,9 +53,7 @@ export async function initializeApp() {
|
|||
});
|
||||
|
||||
await Promise.race([Promise.all(cleanupPromises), timeoutPromise]);
|
||||
} catch (error) {
|
||||
logError('Error during cleanup:', error as Error);
|
||||
}
|
||||
}, 'Error during cleanup');
|
||||
|
||||
cleanupWindow();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
setColorScheme,
|
||||
} from '@/main/modules/config';
|
||||
import { logError } from '@/main/modules/logging';
|
||||
import { safeExecute } from '@/utils/node/logger';
|
||||
import { getSillyTavernManager } from '@/main/modules/sillytavern';
|
||||
import {
|
||||
startFrontend as startOpenWebUIFrontend,
|
||||
|
|
@ -64,8 +65,8 @@ import {
|
|||
import type { FrontendPreference } from '@/types';
|
||||
import { getAppVersion } from '@/utils/node/fs';
|
||||
|
||||
async function launchKoboldCppWithCustomFrontends(args: string[] = []) {
|
||||
try {
|
||||
const launchKoboldCppWithCustomFrontends = async (args: string[] = []) =>
|
||||
(await safeExecute(async () => {
|
||||
const frontendPreference = (await getConfig(
|
||||
'frontendPreference'
|
||||
)) as FrontendPreference;
|
||||
|
|
@ -83,14 +84,10 @@ async function launchKoboldCppWithCustomFrontends(args: string[] = []) {
|
|||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logError('Error in enhanced launch:', error as Error);
|
||||
return {
|
||||
}, 'Error in enhanced launch')) || {
|
||||
success: false,
|
||||
error: (error as Error).message,
|
||||
error: 'Launch failed',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function setupIPCHandlers() {
|
||||
ipcMain.handle('kobold:downloadRelease', async (_, asset) => ({
|
||||
|
|
@ -188,18 +185,18 @@ export function setupIPCHandlers() {
|
|||
};
|
||||
});
|
||||
|
||||
ipcMain.handle('app:showLogsFolder', async () => {
|
||||
try {
|
||||
ipcMain.handle(
|
||||
'app:showLogsFolder',
|
||||
async () =>
|
||||
(await safeExecute(async () => {
|
||||
const logsDir = join(app.getPath('userData'), 'logs');
|
||||
await shell.openPath(logsDir);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logError('Failed to open logs folder:', error as Error);
|
||||
throw new Error(
|
||||
`Failed to open logs folder: ${(error as Error).message}`
|
||||
);
|
||||
}, 'Failed to open logs folder')) || {
|
||||
success: false,
|
||||
error: 'Failed to open logs folder',
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
ipcMain.handle('app:minimizeWindow', () => getMainWindow()?.minimize());
|
||||
|
||||
|
|
@ -239,17 +236,17 @@ export function setupIPCHandlers() {
|
|||
}
|
||||
);
|
||||
|
||||
ipcMain.handle('app:openExternal', async (_, url: string) => {
|
||||
try {
|
||||
ipcMain.handle(
|
||||
'app:openExternal',
|
||||
async (_, url: string) =>
|
||||
(await safeExecute(async () => {
|
||||
await shell.openExternal(url);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logError('Failed to open external URL:', error as Error);
|
||||
throw new Error(
|
||||
`Failed to open external URL: ${(error as Error).message}`
|
||||
);
|
||||
}, 'Failed to open external URL')) || {
|
||||
success: false,
|
||||
error: 'Failed to open external URL',
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
const mainWindow = getMainWindow();
|
||||
if (mainWindow) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { autoUpdater } from 'electron-updater';
|
||||
import { app } from 'electron';
|
||||
import { logError } from '@/main/modules/logging';
|
||||
import { safeExecute } from '@/utils/node/logger';
|
||||
|
||||
export interface UpdateInfo {
|
||||
version: string;
|
||||
|
|
@ -33,25 +34,17 @@ function setupAutoUpdater() {
|
|||
|
||||
setupAutoUpdater();
|
||||
|
||||
export async function checkForUpdates() {
|
||||
try {
|
||||
const result = await autoUpdater.checkForUpdates();
|
||||
return result !== null;
|
||||
} catch (error) {
|
||||
logError('Failed to check for updates:', error as Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const checkForUpdates = async () =>
|
||||
(await safeExecute(
|
||||
() => autoUpdater.checkForUpdates(),
|
||||
'Failed to check for updates'
|
||||
)) !== null;
|
||||
|
||||
export async function downloadUpdate() {
|
||||
try {
|
||||
await autoUpdater.downloadUpdate();
|
||||
return true;
|
||||
} catch (error) {
|
||||
logError('Failed to download update:', error as Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const downloadUpdate = async () =>
|
||||
(await safeExecute(
|
||||
() => autoUpdater.downloadUpdate(),
|
||||
'Failed to download update'
|
||||
)) !== null;
|
||||
|
||||
export function quitAndInstall() {
|
||||
if (updateDownloaded) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { join, dirname } from 'path';
|
||||
import { pathExists } from '@/utils/node/fs';
|
||||
import { logError } from './logging';
|
||||
import { getCurrentBinaryInfo } from './koboldcpp';
|
||||
import { detectGPUCapabilities, detectCPU } from './hardware';
|
||||
import { tryExecute, safeExecute } from '@/utils/node/logger';
|
||||
import type { BackendOption, BackendSupport } from '@/types';
|
||||
|
||||
const backendSupportCache = new Map<string, BackendSupport>();
|
||||
|
|
@ -22,7 +22,7 @@ async function detectBackendSupportFromPath(koboldBinaryPath: string) {
|
|||
cuda: false,
|
||||
};
|
||||
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const binaryDir = dirname(koboldBinaryPath);
|
||||
const internalDir = join(binaryDir, '_internal');
|
||||
|
||||
|
|
@ -60,16 +60,14 @@ async function detectBackendSupportFromPath(koboldBinaryPath: string) {
|
|||
support.noavx2 = noavx2;
|
||||
support.failsafe = failsafe;
|
||||
support.cuda = cuda;
|
||||
} catch (error) {
|
||||
logError('Error detecting backend support:', error as Error);
|
||||
}
|
||||
}, 'Error detecting backend support');
|
||||
|
||||
backendSupportCache.set(koboldBinaryPath, support);
|
||||
return support;
|
||||
}
|
||||
|
||||
export async function detectBackendSupport() {
|
||||
try {
|
||||
export const detectBackendSupport = async () =>
|
||||
(await safeExecute(async () => {
|
||||
const currentBinaryInfo = await getCurrentBinaryInfo();
|
||||
|
||||
if (!currentBinaryInfo?.path) {
|
||||
|
|
@ -77,17 +75,11 @@ export async function detectBackendSupport() {
|
|||
}
|
||||
|
||||
return detectBackendSupportFromPath(currentBinaryInfo.path);
|
||||
} catch (error) {
|
||||
logError('Error detecting current binary backend support:', error as Error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}, 'Error detecting current binary backend support')) || null;
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export async function getAvailableBackends(
|
||||
includeDisabled = false
|
||||
): Promise<BackendOption[]> {
|
||||
try {
|
||||
export async function getAvailableBackends(includeDisabled = false) {
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
const result = await safeExecute(async () => {
|
||||
const [currentBinaryInfo, hardwareCapabilities, cpuCapabilities] =
|
||||
await Promise.all([
|
||||
getCurrentBinaryInfo(),
|
||||
|
|
@ -177,8 +169,7 @@ export async function getAvailableBackends(
|
|||
|
||||
availableBackendsCache.set(cacheKey, backends);
|
||||
return backends;
|
||||
} catch (error) {
|
||||
logError('Failed to get available backends:', error as Error);
|
||||
return [{ value: 'cpu', label: 'CPU' }];
|
||||
}
|
||||
}, 'Failed to get available backends');
|
||||
|
||||
return result || [{ value: 'cpu', label: 'CPU' }];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import type { ChildProcess } from 'child_process';
|
|||
import yauzl from 'yauzl';
|
||||
|
||||
import { logError } from './logging';
|
||||
import { safeExecute } from '@/utils/node/logger';
|
||||
import { sendKoboldOutput } from './window';
|
||||
import { getInstallDir } from './config';
|
||||
import { COMFYUI, SERVER_READY_SIGNALS, GITHUB_API } from '@/constants';
|
||||
|
|
@ -20,7 +21,7 @@ interface ComfyUIVersionInfo {
|
|||
}
|
||||
|
||||
async function getLatestComfyUIVersion(): Promise<ComfyUIVersionInfo | null> {
|
||||
try {
|
||||
return safeExecute(async () => {
|
||||
const response = await fetch(GITHUB_API.COMFYUI_LATEST_COMMIT_URL);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
|
|
@ -33,13 +34,7 @@ async function getLatestComfyUIVersion(): Promise<ComfyUIVersionInfo | null> {
|
|||
sha: data.sha,
|
||||
date: data.commit.committer.date,
|
||||
};
|
||||
} catch (error) {
|
||||
logError(
|
||||
'Failed to fetch latest ComfyUI version',
|
||||
error instanceof Error ? error : undefined
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}, 'Failed to fetch latest ComfyUI version');
|
||||
}
|
||||
|
||||
async function getCurrentComfyUIVersion(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { logError } from '@/main/modules/logging';
|
||||
import { readJsonFile, writeJsonFile } from '@/utils/node/fs';
|
||||
import { safeExecute } from '@/utils/node/logger';
|
||||
import { getConfigDir } from '@/utils/node/path';
|
||||
import { homedir } from 'os';
|
||||
import { join } from 'path';
|
||||
|
|
@ -23,21 +23,19 @@ let config: AppConfig = {};
|
|||
let configPath: string;
|
||||
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const loadedConfig = await readJsonFile<AppConfig>(configPath);
|
||||
return loadedConfig || {};
|
||||
} catch (error) {
|
||||
logError('Error loading config:', error as Error);
|
||||
return {};
|
||||
}
|
||||
const config = await safeExecute(
|
||||
() => readJsonFile<AppConfig>(configPath),
|
||||
'Error loading config'
|
||||
);
|
||||
return config || {};
|
||||
}
|
||||
|
||||
async function saveConfig() {
|
||||
try {
|
||||
await writeJsonFile(configPath, config);
|
||||
} catch (error) {
|
||||
logError('Error saving config:', error as Error);
|
||||
}
|
||||
const success = await safeExecute(
|
||||
() => writeJsonFile(configPath, config),
|
||||
'Error saving config'
|
||||
);
|
||||
return success !== null;
|
||||
}
|
||||
|
||||
export async function initialize() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-comments/disallowComments */
|
||||
import si from 'systeminformation';
|
||||
import { logError } from '@/main/modules/logging';
|
||||
import { safeExecute } from '@/utils/node/logger';
|
||||
import { terminateProcess } from '@/utils/node/process';
|
||||
import { getGPUData } from '@/utils/node/gpu';
|
||||
import type {
|
||||
|
|
@ -23,7 +23,7 @@ export async function detectCPU() {
|
|||
return cpuCapabilitiesCache;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await safeExecute(async () => {
|
||||
const [cpu, flags] = await Promise.all([si.cpu(), si.cpuFlags()]);
|
||||
|
||||
const devices: string[] = [];
|
||||
|
|
@ -34,23 +34,24 @@ export async function detectCPU() {
|
|||
const avx = flags.includes('avx') || flags.includes('AVX');
|
||||
const avx2 = flags.includes('avx2') || flags.includes('AVX2');
|
||||
|
||||
cpuCapabilitiesCache = {
|
||||
const capabilities = {
|
||||
avx,
|
||||
avx2,
|
||||
devices,
|
||||
};
|
||||
|
||||
return cpuCapabilitiesCache;
|
||||
} catch (error) {
|
||||
logError('CPU detection failed:', error as Error);
|
||||
cpuCapabilitiesCache = capabilities;
|
||||
return capabilities;
|
||||
}, 'CPU detection failed');
|
||||
|
||||
const fallbackCapabilities = {
|
||||
avx: false,
|
||||
avx2: false,
|
||||
devices: [],
|
||||
};
|
||||
cpuCapabilitiesCache = fallbackCapabilities;
|
||||
return fallbackCapabilities;
|
||||
}
|
||||
|
||||
cpuCapabilitiesCache = result || fallbackCapabilities;
|
||||
return cpuCapabilitiesCache;
|
||||
}
|
||||
|
||||
export async function detectGPU() {
|
||||
|
|
@ -58,7 +59,7 @@ export async function detectGPU() {
|
|||
return basicGPUInfoCache;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await safeExecute(async () => {
|
||||
const gpuData = await getGPUData();
|
||||
|
||||
let hasAMD = false;
|
||||
|
|
@ -90,23 +91,24 @@ export async function detectGPU() {
|
|||
}
|
||||
}
|
||||
|
||||
basicGPUInfoCache = {
|
||||
const basicInfo = {
|
||||
hasAMD,
|
||||
hasNVIDIA,
|
||||
gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
|
||||
};
|
||||
|
||||
return basicGPUInfoCache;
|
||||
} catch (error) {
|
||||
logError('GPU detection failed:', error as Error);
|
||||
basicGPUInfoCache = basicInfo;
|
||||
return basicInfo;
|
||||
}, 'GPU detection failed');
|
||||
|
||||
const fallbackGPUInfo = {
|
||||
hasAMD: false,
|
||||
hasNVIDIA: false,
|
||||
gpuInfo: ['GPU detection failed'],
|
||||
};
|
||||
basicGPUInfoCache = fallbackGPUInfo;
|
||||
return fallbackGPUInfo;
|
||||
}
|
||||
|
||||
basicGPUInfoCache = result || fallbackGPUInfo;
|
||||
return basicGPUInfoCache;
|
||||
}
|
||||
|
||||
export async function detectGPUCapabilities() {
|
||||
|
|
@ -428,10 +430,9 @@ export async function detectGPUMemory() {
|
|||
return gpuMemoryInfoCache;
|
||||
}
|
||||
|
||||
const memoryInfo: GPUMemoryInfo[] = [];
|
||||
|
||||
try {
|
||||
const result = await safeExecute(async () => {
|
||||
const gpuData = await getGPUData();
|
||||
const memoryInfo: GPUMemoryInfo[] = [];
|
||||
|
||||
for (const gpu of gpuData) {
|
||||
if (gpu.deviceName) {
|
||||
|
|
@ -448,11 +449,9 @@ export async function detectGPUMemory() {
|
|||
}
|
||||
}
|
||||
|
||||
gpuMemoryInfoCache = memoryInfo;
|
||||
} catch (error) {
|
||||
logError('GPU memory detection failed:', error as Error);
|
||||
gpuMemoryInfoCache = [];
|
||||
}
|
||||
return memoryInfo;
|
||||
}, 'GPU memory detection failed');
|
||||
|
||||
gpuMemoryInfoCache = result || [];
|
||||
return gpuMemoryInfoCache;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
setCurrentKoboldBinary,
|
||||
} from './config';
|
||||
import { logError } from './logging';
|
||||
import { tryExecute } from '@/utils/node/logger';
|
||||
import { sendKoboldOutput, getMainWindow, sendToRenderer } from './window';
|
||||
import { PRODUCT_NAME, SERVER_READY_SIGNALS } from '@/constants';
|
||||
import {
|
||||
|
|
@ -207,9 +208,7 @@ export async function downloadRelease(asset: GitHubAsset) {
|
|||
return launcherPath;
|
||||
} catch (error) {
|
||||
logError('Failed to download or unpack binary:', error as Error);
|
||||
throw new Error(
|
||||
`Failed to download or unpack binary: ${(error as Error).message}`
|
||||
);
|
||||
throw new Error('Failed to download or unpack binary');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +231,7 @@ async function unpackKoboldCpp(packedPath: string, unpackDir: string) {
|
|||
}
|
||||
|
||||
async function patchKliteEmbd(unpackedDir: string) {
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const possiblePaths = [
|
||||
join(unpackedDir, '_internal', 'klite.embd'),
|
||||
join(unpackedDir, 'klite.embd'),
|
||||
|
|
@ -276,13 +275,11 @@ async function patchKliteEmbd(unpackedDir: string) {
|
|||
|
||||
await writeFile(kliteEmbdPath, patchedContent, 'utf8');
|
||||
}
|
||||
} catch (error) {
|
||||
logError('Failed to patch klite.embd:', error as Error);
|
||||
}
|
||||
}, 'Failed to patch klite.embd');
|
||||
}
|
||||
|
||||
async function patchKcppSduiEmbd(unpackedDir: string) {
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const possiblePaths = [
|
||||
join(unpackedDir, '_internal', 'kcpp_sdui.embd'),
|
||||
join(unpackedDir, 'kcpp_sdui.embd'),
|
||||
|
|
@ -296,9 +293,7 @@ async function patchKcppSduiEmbd(unpackedDir: string) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logError('Failed to patch kcpp_sdui.embd:', error as Error);
|
||||
}
|
||||
}, 'Failed to patch kcpp_sdui.embd');
|
||||
}
|
||||
|
||||
async function getLauncherPath(unpackedDir: string) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import si from 'systeminformation';
|
||||
import { BrowserWindow } from 'electron';
|
||||
import { platform } from 'process';
|
||||
import { logError } from '@/main/modules/logging';
|
||||
import { getGPUData } from '@/utils/node/gpu';
|
||||
import { tryExecute } from '@/utils/node/logger';
|
||||
|
||||
export interface CpuMetrics {
|
||||
usage: number;
|
||||
|
|
@ -90,7 +90,7 @@ export function stopMonitoring() {
|
|||
}
|
||||
|
||||
async function collectAndSendCpuMetrics() {
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const cpuData = await si.currentLoad();
|
||||
const metrics: CpuMetrics = {
|
||||
usage: Math.round(cpuData.currentLoad),
|
||||
|
|
@ -99,13 +99,11 @@ async function collectAndSendCpuMetrics() {
|
|||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('cpu-metrics', metrics);
|
||||
}
|
||||
} catch (error) {
|
||||
logError('Failed to collect CPU metrics:', error as Error);
|
||||
}
|
||||
}, 'Failed to collect CPU metrics');
|
||||
}
|
||||
|
||||
async function collectAndSendMemoryMetrics() {
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const memData = await si.mem();
|
||||
const usedBytes = memData.active || memData.used;
|
||||
const totalBytes = memData.total;
|
||||
|
|
@ -118,13 +116,11 @@ async function collectAndSendMemoryMetrics() {
|
|||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('memory-metrics', metrics);
|
||||
}
|
||||
} catch (error) {
|
||||
logError('Failed to collect memory metrics:', error as Error);
|
||||
}
|
||||
}, 'Failed to collect memory metrics');
|
||||
}
|
||||
|
||||
async function collectAndSendGpuMetrics() {
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
const gpuData = await getGPUData();
|
||||
const metrics: GpuMetrics = {
|
||||
gpus: gpuData.map((gpuInfo) => ({
|
||||
|
|
@ -142,7 +138,5 @@ async function collectAndSendGpuMetrics() {
|
|||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('gpu-metrics', metrics);
|
||||
}
|
||||
} catch (error) {
|
||||
logError('Failed to collect GPU metrics:', error as Error);
|
||||
}
|
||||
}, 'Failed to collect GPU metrics');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { ChildProcess } from 'child_process';
|
|||
import { join } from 'path';
|
||||
|
||||
import { logError } from './logging';
|
||||
import { safeTryExecute, tryExecute } from '@/utils/node/logger';
|
||||
import { sendKoboldOutput } from './window';
|
||||
import { getInstallDir } from './config';
|
||||
import { OPENWEBUI, SERVER_READY_SIGNALS } from '@/constants';
|
||||
|
|
@ -113,23 +114,19 @@ export async function startFrontend(args: string[]) {
|
|||
|
||||
if (openWebUIProcess.stdout) {
|
||||
openWebUIProcess.stdout.on('data', (data: Buffer) => {
|
||||
try {
|
||||
safeTryExecute(() => {
|
||||
const output = data.toString('utf8');
|
||||
sendKoboldOutput(output, true);
|
||||
} catch (error) {
|
||||
logError('Error processing stdout data:', error as Error);
|
||||
}
|
||||
}, 'Error processing stdout data');
|
||||
});
|
||||
}
|
||||
|
||||
if (openWebUIProcess.stderr) {
|
||||
openWebUIProcess.stderr.on('data', (data: Buffer) => {
|
||||
try {
|
||||
safeTryExecute(() => {
|
||||
const output = data.toString('utf8');
|
||||
sendKoboldOutput(output, true);
|
||||
} catch (error) {
|
||||
logError('Error processing stderr data:', error as Error);
|
||||
}
|
||||
}, 'Error processing stderr data');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -170,14 +167,12 @@ export async function stopFrontend() {
|
|||
if (openWebUIProcess) {
|
||||
sendKoboldOutput('Stopping Open WebUI...');
|
||||
|
||||
try {
|
||||
await terminateProcess(openWebUIProcess, {
|
||||
await tryExecute(async () => {
|
||||
await terminateProcess(openWebUIProcess!, {
|
||||
logError: (message, error) => logError(message, error),
|
||||
});
|
||||
sendKoboldOutput('Open WebUI stopped');
|
||||
} catch (error) {
|
||||
logError('Error stopping Open WebUI:', error as Error);
|
||||
}
|
||||
}, 'Error stopping Open WebUI');
|
||||
|
||||
openWebUIProcess = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { spawn } from 'child_process';
|
||||
import { platform } from 'process';
|
||||
import { logError } from './logging';
|
||||
import { safeExecute } from '@/utils/node/logger';
|
||||
|
||||
const LINUX_PERFORMANCE_APPS = [
|
||||
'resources',
|
||||
|
|
@ -45,8 +45,8 @@ async function tryLaunchCommand(command: string, args: string[] = []) {
|
|||
});
|
||||
}
|
||||
|
||||
export async function openPerformanceManager() {
|
||||
try {
|
||||
export const openPerformanceManager = async () =>
|
||||
(await safeExecute(async () => {
|
||||
switch (platform) {
|
||||
case 'darwin': {
|
||||
const success = await tryLaunchCommand('open', [
|
||||
|
|
@ -87,9 +87,7 @@ export async function openPerformanceManager() {
|
|||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = `Failed to open performance manager: ${(error as Error).message}`;
|
||||
logError(errorMessage, error as Error);
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
}
|
||||
}, 'Failed to open performance manager')) || {
|
||||
success: false,
|
||||
error: 'Failed to open performance manager',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { join } from 'path';
|
|||
import type { ChildProcess } from 'child_process';
|
||||
|
||||
import { logError } from './logging';
|
||||
import { tryExecute } from '@/utils/node/logger';
|
||||
import { sendKoboldOutput } from './window';
|
||||
import { SILLYTAVERN, SERVER_READY_SIGNALS } from '@/constants';
|
||||
import { terminateProcess } from '@/utils/node/process';
|
||||
|
|
@ -166,7 +167,7 @@ async function setupSillyTavernConfig(
|
|||
koboldPort: number,
|
||||
isImageMode: boolean
|
||||
) {
|
||||
try {
|
||||
const success = await tryExecute(async () => {
|
||||
const configPath = getSillyTavernSettingsPath();
|
||||
let settings: Record<string, unknown> = {};
|
||||
|
||||
|
|
@ -221,10 +222,11 @@ async function setupSillyTavernConfig(
|
|||
await writeJsonFile(configPath, settings);
|
||||
|
||||
sendKoboldOutput(`SillyTavern configuration updated successfully!`);
|
||||
} catch (error) {
|
||||
logError('Failed to setup SillyTavern config:', error as Error);
|
||||
}, 'Failed to setup SillyTavern config');
|
||||
|
||||
if (!success) {
|
||||
sendKoboldOutput(
|
||||
`Failed to configure SillyTavern: ${error instanceof Error ? error.message : String(error)}`
|
||||
`Failed to configure SillyTavern. Check logs for details.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -396,11 +398,9 @@ export async function stopFrontend() {
|
|||
|
||||
export async function cleanup() {
|
||||
if (sillyTavernProcess) {
|
||||
try {
|
||||
await tryExecute(async () => {
|
||||
await stopFrontend();
|
||||
} catch (error) {
|
||||
logError('Error during SillyTavernManager cleanup:', error as Error);
|
||||
}
|
||||
}, 'Error during SillyTavernManager cleanup');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +1,13 @@
|
|||
import {
|
||||
createSafeExecute,
|
||||
createTryExecute,
|
||||
createTryExecuteImmediate,
|
||||
} from '@/utils/shared/logger-core';
|
||||
|
||||
export const logError = (message: string, error: Error) => {
|
||||
if (window.electronAPI?.logs?.logError) {
|
||||
window.electronAPI.logs.logError(message, error);
|
||||
}
|
||||
};
|
||||
|
||||
export const safeExecute = async <T>(
|
||||
operation: () => Promise<T>,
|
||||
errorMessage: string
|
||||
): Promise<T | null> => {
|
||||
try {
|
||||
return operation();
|
||||
} catch (error) {
|
||||
logError(errorMessage, error as Error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const tryExecute = async (
|
||||
operation: () => Promise<void>,
|
||||
errorMessage: string
|
||||
) => {
|
||||
try {
|
||||
await operation();
|
||||
return true;
|
||||
} catch (error) {
|
||||
logError(errorMessage, error as Error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
export const safeExecute = createSafeExecute(logError);
|
||||
export const tryExecute = createTryExecute(logError);
|
||||
export const tryExecuteImmediate = createTryExecuteImmediate(logError);
|
||||
|
|
|
|||
10
src/utils/node/logger.ts
Normal file
10
src/utils/node/logger.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { logError } from '@/main/modules/logging';
|
||||
import {
|
||||
createSafeExecute,
|
||||
createTryExecute,
|
||||
createSafeTryExecute,
|
||||
} from '@/utils/shared/logger-core';
|
||||
|
||||
export const safeExecute = createSafeExecute(logError);
|
||||
export const tryExecute = createTryExecute(logError);
|
||||
export const safeTryExecute = createSafeTryExecute(logError);
|
||||
46
src/utils/shared/logger-core.ts
Normal file
46
src/utils/shared/logger-core.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
type LoggerFunction = (message: string, error: Error) => void;
|
||||
|
||||
export const createSafeExecute =
|
||||
(logger: LoggerFunction) =>
|
||||
async <T>(operation: () => Promise<T>, errorMessage: string) => {
|
||||
try {
|
||||
return operation();
|
||||
} catch (error) {
|
||||
logger(errorMessage, error as Error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const createTryExecute =
|
||||
(logger: LoggerFunction) =>
|
||||
async (operation: () => Promise<void>, errorMessage: string) => {
|
||||
try {
|
||||
await operation();
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger(errorMessage, error as Error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const createSafeTryExecute =
|
||||
(logger: LoggerFunction) =>
|
||||
<T>(operation: () => T, errorMessage: string) => {
|
||||
try {
|
||||
return operation();
|
||||
} catch (error) {
|
||||
logger(errorMessage, error as Error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const createTryExecuteImmediate =
|
||||
(logger: LoggerFunction) => (operation: () => void, errorMessage: string) => {
|
||||
try {
|
||||
operation();
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger(errorMessage, error as Error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { logError } from '@/utils/logger';
|
||||
import { tryExecuteImmediate } from '@/utils/logger';
|
||||
|
||||
export const handleTerminalOutput = (prevContent: string, newData: string) => {
|
||||
try {
|
||||
const result = tryExecuteImmediate(() => {
|
||||
if (newData.includes('\r')) {
|
||||
const hasStandaloneCarriageReturns = /\r(?!\n)/g.test(newData);
|
||||
|
||||
|
|
@ -28,10 +28,9 @@ export const handleTerminalOutput = (prevContent: string, newData: string) => {
|
|||
}
|
||||
|
||||
return prevContent + newData;
|
||||
} catch (err) {
|
||||
logError('Terminal Basic Error', err as Error);
|
||||
return prevContent + newData;
|
||||
}
|
||||
}, 'Terminal Basic Error');
|
||||
|
||||
return result ?? prevContent + newData;
|
||||
};
|
||||
|
||||
const URL_REGEX = /(https?:\/\/[^\s<>"{}|\\^`[\]]+)/gi;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue