mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-04 04:04:44 -07:00
prettify binary names, minor changes and improvements
This commit is contained in:
parent
1499445762
commit
d7e09f2ea5
23 changed files with 112 additions and 113 deletions
10
README.md
10
README.md
|
|
@ -8,7 +8,7 @@ A desktop app to easily run Large Language Models locally.
|
||||||
|
|
||||||
## Core Features
|
## Core Features
|
||||||
|
|
||||||
- **Run LLMs locally** powered by [KoboldCpp](https://github.com/LostRuins/koboldcpp) which itself is a highly modified fork of [llama.cpp](https://github.com/ggml-org/llama.cpp)
|
- **Run LLMs locally** powered by a highly modified fork of [llama.cpp](https://github.com/ggml-org/llama.cpp)
|
||||||
- **Cross-platform desktop app** - Native support for Windows, macOS, and Linux (including Wayland)
|
- **Cross-platform desktop app** - Native support for Windows, macOS, and Linux (including Wayland)
|
||||||
- **Automatic updates** - Download and keep your KoboldCpp binary up-to-date effortlessly
|
- **Automatic updates** - Download and keep your KoboldCpp binary up-to-date effortlessly
|
||||||
- **Smart process management** - Prevents runaway background processes and system resource waste
|
- **Smart process management** - Prevents runaway background processes and system resource waste
|
||||||
|
|
@ -150,8 +150,12 @@ You can use the CLI mode on Windows in exactly the same way as in the Linux/macO
|
||||||
yarn dev
|
yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
- migrate the project to Tauri from Electron?
|
||||||
|
- transition to using llama.cpp binaries directly instead of running them indirectly through koboldcpp?
|
||||||
|
- seamless support for more frontends?
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
AGPL v3 License - see LICENSE file for details
|
AGPL v3 License - see LICENSE file for details
|
||||||
|
|
||||||
# test
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "gerbil",
|
"name": "gerbil",
|
||||||
"productName": "Gerbil",
|
"productName": "Gerbil",
|
||||||
"version": "0.9.9",
|
"version": "1.0.0",
|
||||||
"description": "Run Large Language Models locally",
|
"description": "Run Large Language Models locally",
|
||||||
"main": "out/main/index.js",
|
"main": "out/main/index.js",
|
||||||
"homepage": "./",
|
"homepage": "./",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { Checkbox, Group } from '@mantine/core';
|
import { Checkbox, Group } from '@mantine/core';
|
||||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||||
import styles from '@/styles/layout.module.css';
|
|
||||||
|
|
||||||
interface CheckboxWithTooltipProps {
|
interface CheckboxWithTooltipProps {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
|
|
@ -17,7 +16,7 @@ export const CheckboxWithTooltip = ({
|
||||||
tooltip,
|
tooltip,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}: CheckboxWithTooltipProps) => (
|
}: CheckboxWithTooltipProps) => (
|
||||||
<div className={styles.minWidth200}>
|
<div style={{ minWidth: '12.5rem' }}>
|
||||||
<Group gap="xs" align="center">
|
<Group gap="xs" align="center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={checked}
|
checked={checked}
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,7 @@ const COMMAND_LINE_ARGUMENTS: ArgumentInfo[] = [
|
||||||
{
|
{
|
||||||
flag: '--cli',
|
flag: '--cli',
|
||||||
description:
|
description:
|
||||||
'Does not launch KoboldCpp HTTP server. Instead, enables KoboldCpp from the command line, accepting interactive console input and displaying responses to the terminal.',
|
'Does not launch the HTTP server. Instead, enables input from the command line, accepting interactive console input and displaying responses to the terminal.',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
category: 'Advanced',
|
category: 'Advanced',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { Download } from 'lucide-react';
|
import { Download } from 'lucide-react';
|
||||||
import { MouseEvent } from 'react';
|
import { MouseEvent } from 'react';
|
||||||
import styles from '@/styles/layout.module.css';
|
import { pretifyBinName } from '@/utils/assets';
|
||||||
|
|
||||||
interface DownloadCardProps {
|
interface DownloadCardProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -126,10 +126,10 @@ export const DownloadCard = ({
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Group justify="space-between" align="center">
|
<Group justify="space-between" align="center">
|
||||||
<div className={styles.flex1}>
|
<div style={{ flex: 1 }}>
|
||||||
<Group gap="xs" align="center" mb="xs">
|
<Group gap="xs" align="center" mb="xs">
|
||||||
<Text fw={500} size="sm">
|
<Text fw={500} size="sm">
|
||||||
{name}
|
{pretifyBinName(name)}
|
||||||
</Text>
|
</Text>
|
||||||
{isCurrent && (
|
{isCurrent && (
|
||||||
<Badge variant="light" color="blue" size="sm">
|
<Badge variant="light" color="blue" size="sm">
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { Group, TextInput, Button } from '@mantine/core';
|
||||||
import { File, Search } from 'lucide-react';
|
import { File, Search } from 'lucide-react';
|
||||||
import { LabelWithTooltip } from '@/components/LabelWithTooltip';
|
import { LabelWithTooltip } from '@/components/LabelWithTooltip';
|
||||||
import { getInputValidationState } from '@/utils/validation';
|
import { getInputValidationState } from '@/utils/validation';
|
||||||
import styles from '@/styles/layout.module.css';
|
|
||||||
|
|
||||||
interface ModelFileFieldProps {
|
interface ModelFileFieldProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -41,7 +40,7 @@ export const ModelFileField = ({
|
||||||
<div>
|
<div>
|
||||||
<LabelWithTooltip label={label} tooltip={tooltip} />
|
<LabelWithTooltip label={label} tooltip={tooltip} />
|
||||||
<Group gap="xs" align="flex-start">
|
<Group gap="xs" align="flex-start">
|
||||||
<div className={styles.flex1}>
|
<div style={{ flex: 1 }}>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
value={value}
|
value={value}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export const ServerTab = ({
|
||||||
>
|
>
|
||||||
<Stack align="center" gap="md" mt="xl">
|
<Stack align="center" gap="md" mt="xl">
|
||||||
<Text c="dimmed" size="lg">
|
<Text c="dimmed" size="lg">
|
||||||
Waiting for the KoboldCpp server to start...
|
Waiting for the server to start...
|
||||||
</Text>
|
</Text>
|
||||||
<Text c="dimmed" size="sm">
|
<Text c="dimmed" size="sm">
|
||||||
The {isImageGenerationMode ? 'image generation' : 'chat'} interface
|
The {isImageGenerationMode ? 'image generation' : 'chat'} interface
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import {
|
||||||
useComputedColorScheme,
|
useComputedColorScheme,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { ChevronDown } from 'lucide-react';
|
import { ChevronDown } from 'lucide-react';
|
||||||
import styles from '@/styles/layout.module.css';
|
|
||||||
import { SERVER_READY_SIGNALS, TITLEBAR_HEIGHT } from '@/constants';
|
import { SERVER_READY_SIGNALS, TITLEBAR_HEIGHT } from '@/constants';
|
||||||
import { handleTerminalOutput, processTerminalContent } from '@/utils/terminal';
|
import { handleTerminalOutput, processTerminalContent } from '@/utils/terminal';
|
||||||
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
||||||
|
|
@ -120,14 +119,19 @@ export const TerminalTab = ({
|
||||||
ref={scrollAreaRef}
|
ref={scrollAreaRef}
|
||||||
viewportRef={viewportRef}
|
viewportRef={viewportRef}
|
||||||
onScrollPositionChange={handleScroll}
|
onScrollPositionChange={handleScroll}
|
||||||
className={styles.terminalScrollArea}
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
fontFamily: 'Monaco, Menlo, Ubuntu Mono, monospace',
|
||||||
|
fontSize: '0.875em',
|
||||||
|
lineHeight: 1.4,
|
||||||
|
}}
|
||||||
scrollbarSize={8}
|
scrollbarSize={8}
|
||||||
offsetScrollbars={false}
|
offsetScrollbars={false}
|
||||||
>
|
>
|
||||||
<Box p="md">
|
<Box p="md">
|
||||||
{terminalContent.length === 0 ? (
|
{terminalContent.length === 0 ? (
|
||||||
<Text c="dimmed" style={{ fontFamily: 'inherit' }}>
|
<Text c="dimmed" style={{ fontFamily: 'inherit' }}>
|
||||||
Starting KoboldCpp...
|
Starting...
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ export const AdvancedTab = () => {
|
||||||
<Text size="sm" fw={500}>
|
<Text size="sm" fw={500}>
|
||||||
Additional arguments
|
Additional arguments
|
||||||
</Text>
|
</Text>
|
||||||
<InfoTooltip label="Additional command line arguments to pass to the KoboldCPP binary. Leave this empty if you don't know what they are." />
|
<InfoTooltip label="Additional command line arguments to pass to the binary. Leave this empty if you don't know what they are." />
|
||||||
</Group>
|
</Group>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { Stack, Text, Group, Button, Select } from '@mantine/core';
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Save, File, Plus, Check } from 'lucide-react';
|
import { Save, File, Plus, Check } from 'lucide-react';
|
||||||
import type { ConfigFile } from '@/types';
|
import type { ConfigFile } from '@/types';
|
||||||
import styles from '@/styles/layout.module.css';
|
|
||||||
import { CreateConfigModal } from './CreateConfigModal';
|
import { CreateConfigModal } from './CreateConfigModal';
|
||||||
|
|
||||||
interface ConfigFileManagerProps {
|
interface ConfigFileManagerProps {
|
||||||
|
|
@ -67,7 +66,7 @@ export const ConfigFileManager = ({
|
||||||
Configuration
|
Configuration
|
||||||
</Text>
|
</Text>
|
||||||
<Group gap="xs" align="flex-end">
|
<Group gap="xs" align="flex-end">
|
||||||
<div className={styles.flex1}>
|
<div style={{ flex: 1 }}>
|
||||||
<Select
|
<Select
|
||||||
placeholder="Select a configuration file"
|
placeholder="Select a configuration file"
|
||||||
value={selectedFile}
|
value={selectedFile}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export const NetworkTab = () => {
|
||||||
<Text size="sm" fw={500}>
|
<Text size="sm" fw={500}>
|
||||||
Host
|
Host
|
||||||
</Text>
|
</Text>
|
||||||
<InfoTooltip label="The hostname or IP address on which KoboldCpp will bind its webserver to." />
|
<InfoTooltip label="The hostname or IP address to bind the webserver to." />
|
||||||
</Group>
|
</Group>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="localhost"
|
placeholder="localhost"
|
||||||
|
|
@ -49,7 +49,7 @@ export const NetworkTab = () => {
|
||||||
<Text size="sm" fw={500}>
|
<Text size="sm" fw={500}>
|
||||||
Port
|
Port
|
||||||
</Text>
|
</Text>
|
||||||
<InfoTooltip label="The port number on which KoboldCpp will listen for connections. Leave empty to use default port 5001." />
|
<InfoTooltip label="The port number on which the server will listen for connections. Leave empty to use default port 5001." />
|
||||||
</Group>
|
</Group>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="5001"
|
placeholder="5001"
|
||||||
|
|
@ -105,7 +105,7 @@ export const NetworkTab = () => {
|
||||||
checked={remotetunnel}
|
checked={remotetunnel}
|
||||||
onChange={handleRemotetunnelChange}
|
onChange={handleRemotetunnelChange}
|
||||||
label="Remote Tunnel"
|
label="Remote Tunnel"
|
||||||
tooltip="Creates a trycloudflare tunnel. Allows you to access koboldcpp from other devices over an internet URL."
|
tooltip="Creates a trycloudflare tunnel. Allows you to access your server from other devices over an internet URL."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CheckboxWithTooltip
|
<CheckboxWithTooltip
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,10 @@ import {
|
||||||
Group,
|
Group,
|
||||||
List,
|
List,
|
||||||
ThemeIcon,
|
ThemeIcon,
|
||||||
Anchor,
|
Image,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { Check, ExternalLink } from 'lucide-react';
|
import { Check } from 'lucide-react';
|
||||||
|
import iconUrl from '/icon.png';
|
||||||
|
|
||||||
interface WelcomeScreenProps {
|
interface WelcomeScreenProps {
|
||||||
onGetStarted: () => void;
|
onGetStarted: () => void;
|
||||||
|
|
@ -23,9 +24,12 @@ export const WelcomeScreen = ({ onGetStarted }: WelcomeScreenProps) => (
|
||||||
<Card withBorder radius="md" shadow="sm" p="xl">
|
<Card withBorder radius="md" shadow="sm" p="xl">
|
||||||
<Stack gap="lg" align="center">
|
<Stack gap="lg" align="center">
|
||||||
<Stack gap="md" align="center">
|
<Stack gap="md" align="center">
|
||||||
|
<Group gap="md" mr="xl" align="center">
|
||||||
|
<Image src={iconUrl} alt={PRODUCT_NAME} w={36} h={36} />
|
||||||
<Title order={1} ta="center">
|
<Title order={1} ta="center">
|
||||||
{PRODUCT_NAME}
|
{PRODUCT_NAME}
|
||||||
</Title>
|
</Title>
|
||||||
|
</Group>
|
||||||
<Text size="lg" c="dimmed" ta="center" maw={600}>
|
<Text size="lg" c="dimmed" ta="center" maw={600}>
|
||||||
Run Large Language Models locally
|
Run Large Language Models locally
|
||||||
</Text>
|
</Text>
|
||||||
|
|
@ -84,44 +88,9 @@ export const WelcomeScreen = ({ onGetStarted }: WelcomeScreenProps) => (
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Button size="lg" onClick={onGetStarted}>
|
<Button size="lg" mt="lg" onClick={onGetStarted}>
|
||||||
Get Started
|
Get Started
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Group gap="lg" mt="md">
|
|
||||||
<Anchor
|
|
||||||
href="#"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
window.electronAPI.app.openExternal(
|
|
||||||
'https://github.com/LostRuins/koboldcpp'
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
size="sm"
|
|
||||||
c="dimmed"
|
|
||||||
>
|
|
||||||
<Group gap={4} align="center">
|
|
||||||
<span>About KoboldCpp</span>
|
|
||||||
<ExternalLink size={12} />
|
|
||||||
</Group>
|
|
||||||
</Anchor>
|
|
||||||
<Anchor
|
|
||||||
href="#"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
window.electronAPI.app.openExternal(
|
|
||||||
'https://github.com/lone-cloud/gerbil'
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
size="sm"
|
|
||||||
c="dimmed"
|
|
||||||
>
|
|
||||||
<Group gap={4} align="center">
|
|
||||||
<span>About Gerbil</span>
|
|
||||||
<ExternalLink size={12} />
|
|
||||||
</Group>
|
|
||||||
</Anchor>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,10 @@ import {
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
rem,
|
rem,
|
||||||
|
ActionIcon,
|
||||||
|
Tooltip,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { Github, FolderOpen } from 'lucide-react';
|
import { Github, FolderOpen, Copy } from 'lucide-react';
|
||||||
import { safeExecute } from '@/utils/logger';
|
import { safeExecute } from '@/utils/logger';
|
||||||
import type { VersionInfo } from '@/types/electron';
|
import type { VersionInfo } from '@/types/electron';
|
||||||
import { PRODUCT_NAME } from '@/constants';
|
import { PRODUCT_NAME } from '@/constants';
|
||||||
|
|
@ -50,6 +52,22 @@ export const AboutTab = () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const copyVersionInfo = async () => {
|
||||||
|
const info = [
|
||||||
|
`${PRODUCT_NAME} v${versionInfo.appVersion}`,
|
||||||
|
`Electron: ${versionInfo.electronVersion}`,
|
||||||
|
`Node.js: ${versionInfo.nodeVersion}`,
|
||||||
|
`Chromium: ${versionInfo.chromeVersion}`,
|
||||||
|
`V8: ${versionInfo.v8Version}`,
|
||||||
|
`OS: ${versionInfo.platform} ${versionInfo.arch} (${versionInfo.osVersion})`,
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
await safeExecute(
|
||||||
|
() => navigator.clipboard.writeText(info),
|
||||||
|
'Failed to copy version info'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Card withBorder radius="md" p="xs">
|
<Card withBorder radius="md" p="xs">
|
||||||
|
|
@ -118,7 +136,23 @@ export const AboutTab = () => {
|
||||||
</Group>
|
</Group>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card withBorder radius="md" p="xs">
|
<Card withBorder radius="md" p="xs" style={{ position: 'relative' }}>
|
||||||
|
<Tooltip label="Copy info">
|
||||||
|
<ActionIcon
|
||||||
|
variant="subtle"
|
||||||
|
size="sm"
|
||||||
|
onClick={copyVersionInfo}
|
||||||
|
aria-label="Copy Info"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 8,
|
||||||
|
right: 8,
|
||||||
|
zIndex: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Copy style={{ width: rem(14), height: rem(14) }} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
{versionItems.map((item, index) => (
|
{versionItems.map((item, index) => (
|
||||||
<Group key={index} gap="md" align="center" wrap="nowrap">
|
<Group key={index} gap="md" align="center" wrap="nowrap">
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import {
|
||||||
Anchor,
|
Anchor,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { Folder, FolderOpen, Monitor } from 'lucide-react';
|
import { Folder, FolderOpen, Monitor } from 'lucide-react';
|
||||||
import styles from '@/styles/layout.module.css';
|
|
||||||
import type { FrontendPreference } from '@/types';
|
import type { FrontendPreference } from '@/types';
|
||||||
import { FRONTENDS } from '@/constants';
|
import { FRONTENDS } from '@/constants';
|
||||||
|
|
||||||
|
|
@ -39,7 +38,7 @@ export const GeneralTab = () => {
|
||||||
|
|
||||||
const frontendConfigs: FrontendConfig[] = useMemo(
|
const frontendConfigs: FrontendConfig[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{ value: 'koboldcpp', label: 'KoboldCpp (Built-in)' },
|
{ value: 'koboldcpp', label: 'Built-in Interface' },
|
||||||
{
|
{
|
||||||
value: 'sillytavern',
|
value: 'sillytavern',
|
||||||
label: FRONTENDS.SILLYTAVERN,
|
label: FRONTENDS.SILLYTAVERN,
|
||||||
|
|
@ -195,7 +194,7 @@ export const GeneralTab = () => {
|
||||||
value={installDir}
|
value={installDir}
|
||||||
readOnly
|
readOnly
|
||||||
placeholder="Default installation directory"
|
placeholder="Default installation directory"
|
||||||
className={styles.flex1}
|
style={{ flex: 1 }}
|
||||||
leftSection={<Folder style={{ width: rem(16), height: rem(16) }} />}
|
leftSection={<Folder style={{ width: rem(16), height: rem(16) }} />}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,7 @@ export const useLaunchLogic = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error('Error launching KoboldCpp:', err as Error);
|
error('Error launching:', err as Error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLaunching(false);
|
setIsLaunching(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ const checkGpuWarnings = async (
|
||||||
warnings.push({
|
warnings.push({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message:
|
message:
|
||||||
'Your KoboldCpp binary supports CUDA and you have an NVIDIA GPU, but CUDA runtime is not detected on your system.',
|
'Your binary supports CUDA and you have an NVIDIA GPU, but CUDA runtime is not detected on your system.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ const checkGpuWarnings = async (
|
||||||
warnings.push({
|
warnings.push({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message:
|
message:
|
||||||
'Your KoboldCpp binary supports ROCm and you have an AMD GPU, but ROCm runtime is not detected on your system.',
|
'Your binary supports ROCm and you have an AMD GPU, but ROCm runtime is not detected on your system.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,14 @@ export class LightweightCliHandler {
|
||||||
|
|
||||||
if (!currentBinary) {
|
if (!currentBinary) {
|
||||||
console.error(
|
console.error(
|
||||||
'Error: No KoboldCpp binary found. Please run the GUI first to download KoboldCpp.'
|
'Error: No binary found. Please run the GUI first to download the binary.'
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await pathExists(currentBinary))) {
|
if (!(await pathExists(currentBinary))) {
|
||||||
console.error(`Error: KoboldCpp binary not found at: ${currentBinary}`);
|
console.error(`Error: Binary not found at: ${currentBinary}`);
|
||||||
console.error('Please run the GUI to download or reconfigure KoboldCpp.');
|
console.error('Please run the GUI to download and configure the binary.');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,12 +75,12 @@ export class LightweightCliHandler {
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on('error', (error) => {
|
child.on('error', (error) => {
|
||||||
console.error(`Failed to start KoboldCpp: ${error.message}`);
|
console.error(`Failed to start: ${error.message}`);
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSignal = async () => {
|
const handleSignal = async () => {
|
||||||
console.log('\nReceived termination signal, terminating KoboldCpp...');
|
console.log('\nReceived termination signal, terminating...');
|
||||||
if (!child.killed) {
|
if (!child.killed) {
|
||||||
await terminateProcess(child, {
|
await terminateProcess(child, {
|
||||||
timeoutMs: 5000,
|
timeoutMs: 5000,
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ export class KoboldCppManager {
|
||||||
try {
|
try {
|
||||||
if (this.koboldProcess && !this.koboldProcess.killed) {
|
if (this.koboldProcess && !this.koboldProcess.killed) {
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
'Stopping KoboldCpp process before update...'
|
'Stopping process before update...'
|
||||||
);
|
);
|
||||||
await this.cleanup();
|
await this.cleanup();
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
|
@ -91,7 +91,7 @@ export class KoboldCppManager {
|
||||||
);
|
);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Cannot update: Failed to remove existing installation. ` +
|
`Cannot update: Failed to remove existing installation. ` +
|
||||||
`Please ensure KoboldCpp is stopped and try again. ` +
|
`Please ensure the server is stopped and try again. ` +
|
||||||
`Error: ${(error as Error).message}`
|
`Error: ${(error as Error).message}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,7 @@ export class KoboldCppManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!launcherPath || !(await pathExists(launcherPath))) {
|
if (!launcherPath || !(await pathExists(launcherPath))) {
|
||||||
throw new Error('Failed to find or create koboldcpp launcher');
|
throw new Error('Failed to find or create launcher');
|
||||||
}
|
}
|
||||||
|
|
||||||
return launcherPath;
|
return launcherPath;
|
||||||
|
|
@ -633,8 +633,8 @@ export class KoboldCppManager {
|
||||||
if (!currentVersion || !(await pathExists(currentVersion.path))) {
|
if (!currentVersion || !(await pathExists(currentVersion.path))) {
|
||||||
const rawPath = this.configManager.getCurrentKoboldBinary();
|
const rawPath = this.configManager.getCurrentKoboldBinary();
|
||||||
const error = currentVersion
|
const error = currentVersion
|
||||||
? `KoboldCpp binary file does not exist at path: ${currentVersion.path}`
|
? `Binary file does not exist at path: ${currentVersion.path}`
|
||||||
: 'No KoboldCpp version configured';
|
: 'No version configured';
|
||||||
|
|
||||||
this.logManager.logError(
|
this.logManager.logError(
|
||||||
`Launch failed: ${error}. Raw config path: "${rawPath}", Current version: ${JSON.stringify(currentVersion)}`
|
`Launch failed: ${error}. Raw config path: "${rawPath}", Current version: ${JSON.stringify(currentVersion)}`
|
||||||
|
|
@ -717,10 +717,7 @@ export class KoboldCppManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on('error', (error) => {
|
child.on('error', (error) => {
|
||||||
this.logManager.logError(
|
this.logManager.logError(`Process error: ${error.message}`, error);
|
||||||
`KoboldCpp process error: ${error.message}`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
|
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
`\n[ERROR] Process error: ${error.message}\n`
|
`\n[ERROR] Process error: ${error.message}\n`
|
||||||
|
|
@ -736,7 +733,7 @@ export class KoboldCppManager {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = (error as Error).message;
|
const errorMessage = (error as Error).message;
|
||||||
this.logManager.logError(
|
this.logManager.logError(
|
||||||
`Failed to launch KoboldCpp: ${errorMessage}`,
|
`Failed to launch: ${errorMessage}`,
|
||||||
error as Error
|
error as Error
|
||||||
);
|
);
|
||||||
return { success: false, error: errorMessage };
|
return { success: false, error: errorMessage };
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import { app } from 'electron';
|
import { app } from 'electron';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import winston from 'winston';
|
import { createLogger, format, type Logger } from 'winston';
|
||||||
import 'winston-daily-rotate-file';
|
import DailyRotateFile from 'winston-daily-rotate-file';
|
||||||
|
|
||||||
export class LogManager {
|
export class LogManager {
|
||||||
private logger: winston.Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const logsDir = join(app.getPath('userData'), 'logs');
|
const logsDir = join(app.getPath('userData'), 'logs');
|
||||||
|
|
||||||
this.logger = winston.createLogger({
|
this.logger = createLogger({
|
||||||
level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
|
level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
|
||||||
format: winston.format.combine(
|
format: format.combine(
|
||||||
winston.format.timestamp(),
|
format.timestamp(),
|
||||||
winston.format.printf(({ timestamp, level, message, error }) => {
|
format.printf(({ timestamp, level, message, error }) => {
|
||||||
const processInfo = `[${process.type || 'unknown'}:${process.pid}]`;
|
const processInfo = `[${process.type || 'unknown'}:${process.pid}]`;
|
||||||
let logEntry = `${timestamp} ${processInfo} [${level.toUpperCase()}] ${message}`;
|
let logEntry = `${timestamp} ${processInfo} [${level.toUpperCase()}] ${message}`;
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ export class LogManager {
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
transports: [
|
transports: [
|
||||||
new winston.transports.DailyRotateFile({
|
new DailyRotateFile({
|
||||||
filename: join(logsDir, 'gerbil-%DATE%.log'),
|
filename: join(logsDir, 'gerbil-%DATE%.log'),
|
||||||
datePattern: 'YYYY-MM-DD',
|
datePattern: 'YYYY-MM-DD',
|
||||||
maxSize: '10m',
|
maxSize: '10m',
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ export class OpenWebUIManager {
|
||||||
await this.stopFrontend();
|
await this.stopFrontend();
|
||||||
|
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
`Preparing Open WebUI to connect to KoboldCpp at ${koboldHost}:${koboldPort}...`
|
`Preparing Open WebUI to connect at ${koboldHost}:${koboldPort}...`
|
||||||
);
|
);
|
||||||
|
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
|
|
@ -252,13 +252,13 @@ export class OpenWebUIManager {
|
||||||
await this.waitForOpenWebUIToStart();
|
await this.waitForOpenWebUIToStart();
|
||||||
|
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
`Open WebUI is ready and auto-configured for KoboldCpp!`
|
`Open WebUI is ready and auto-configured!`
|
||||||
);
|
);
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
`Access Open WebUI at: http://localhost:${config.port}`
|
`Access Open WebUI at: http://localhost:${config.port}`
|
||||||
);
|
);
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
`KoboldCpp connection: ${koboldUrl}/v1 (auto-configured)`
|
`Connection: ${koboldUrl}/v1 (auto-configured)`
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logManager.logError(
|
this.logManager.logError(
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,7 @@ export class SillyTavernManager {
|
||||||
textgenSettings.type = 'koboldcpp';
|
textgenSettings.type = 'koboldcpp';
|
||||||
|
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
`Configured SillyTavern for text generation with KoboldCpp at ${koboldUrl}`
|
`Configured SillyTavern for text generation at ${koboldUrl}`
|
||||||
);
|
);
|
||||||
|
|
||||||
await writeJsonFile(configPath, settings);
|
await writeJsonFile(configPath, settings);
|
||||||
|
|
@ -461,7 +461,7 @@ export class SillyTavernManager {
|
||||||
await this.stopFrontend();
|
await this.stopFrontend();
|
||||||
|
|
||||||
this.windowManager.sendKoboldOutput(
|
this.windowManager.sendKoboldOutput(
|
||||||
`Preparing SillyTavern to connect to KoboldCpp at ${koboldHost}:${koboldPort}...`
|
`Preparing SillyTavern to connect at ${koboldHost}:${koboldPort}...`
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.ensureSillyTavernSettings();
|
await this.ensureSillyTavernSettings();
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
.minWidth200 {
|
|
||||||
min-width: 12.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex1 {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminalScrollArea {
|
|
||||||
flex: 1;
|
|
||||||
font-family: Monaco, Menlo, 'Ubuntu Mono', monospace;
|
|
||||||
font-size: 0.875em;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
@ -53,3 +53,12 @@ export const sortDownloadsByType = <T extends { name: string }>(
|
||||||
|
|
||||||
return a.name.localeCompare(b.name);
|
return a.name.localeCompare(b.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const pretifyBinName = (binName: string) =>
|
||||||
|
binName
|
||||||
|
.replace('koboldcpp-', '')
|
||||||
|
.replace('-x64', ' (x64) ')
|
||||||
|
.replace(' -', ' ')
|
||||||
|
.replace('rocm', '- ROCm')
|
||||||
|
.replace('nocuda', '- NoCUDA')
|
||||||
|
.replace('oldpc', '- OldPC');
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue