mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-04 12:13:28 -07:00
windows fixes, reformat everything
This commit is contained in:
parent
d04545f5ba
commit
cd54275354
14 changed files with 268 additions and 171 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -10,4 +10,5 @@ Thumbs.db
|
||||||
|
|
||||||
# Yarn
|
# Yarn
|
||||||
.yarn/
|
.yarn/
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
package-lock.json
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,11 @@ A koboldcpp manager. <!-- markdownlint-disable MD033 -->
|
||||||
yarn dev
|
yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### Linux Wayland support
|
### Windows ROCm Support
|
||||||
|
|
||||||
Additional configurations have been written to help with ideal Wayland support, but as per current Electron guidelines, the user should set `ELECTRON_OZONE_PLATFORM_HINT` to `wayland` in their environment variable according to the [Electron Environment Variables documentation](https://www.electronjs.org/docs/latest/api/environment-variables#electron_ozone_platform_hint-linux).
|
There is ROCm Windows support maintained by YellowRoseCx in a separate fork.
|
||||||
|
Unfortunately it does not properly support unpacking, which would greatly diminish its performance and provide a poor UX when used alongside this app.
|
||||||
|
For Friendly Kobold to work with this fork, this issue must be fixed first: https://github.com/YellowRoseCx/koboldcpp-rocm/issues/129
|
||||||
|
|
||||||
### Future features
|
### Future features
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
"hipblas",
|
"hipblas",
|
||||||
"kcpps",
|
"kcpps",
|
||||||
"kcppt",
|
"kcppt",
|
||||||
|
"koboldai",
|
||||||
"KoboldAI",
|
"KoboldAI",
|
||||||
"KOBOLDAI",
|
"KOBOLDAI",
|
||||||
"koboldcpp",
|
"koboldcpp",
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
"ai",
|
"ai",
|
||||||
"llm"
|
"llm"
|
||||||
],
|
],
|
||||||
"author": "Egor Philippov",
|
"author": "lone-cloud",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cspell/eslint-plugin": "^9.2.0",
|
"@cspell/eslint-plugin": "^9.2.0",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
Text,
|
Text,
|
||||||
|
|
@ -6,11 +6,10 @@ import {
|
||||||
Loader,
|
Loader,
|
||||||
Stack,
|
Stack,
|
||||||
Container,
|
Container,
|
||||||
Badge,
|
Anchor,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { DownloadCard } from '@/components/DownloadCard';
|
import { DownloadCard } from '@/components/DownloadCard';
|
||||||
import { StyledTooltip } from '@/components/StyledTooltip';
|
import { getPlatformDisplayName, formatDownloadSize } from '@/utils';
|
||||||
import { getPlatformDisplayName, formatFileSizeInMB } from '@/utils';
|
|
||||||
import {
|
import {
|
||||||
isAssetRecommended,
|
isAssetRecommended,
|
||||||
sortAssetsByRecommendation,
|
sortAssetsByRecommendation,
|
||||||
|
|
@ -38,6 +37,7 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
||||||
'asset' | 'rocm' | null
|
'asset' | 'rocm' | null
|
||||||
>(null);
|
>(null);
|
||||||
const [downloadingAsset, setDownloadingAsset] = useState<string | null>(null);
|
const [downloadingAsset, setDownloadingAsset] = useState<string | null>(null);
|
||||||
|
const downloadingItemRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const loading = loadingPlatform || loadingRemote;
|
const loading = loadingPlatform || loadingRemote;
|
||||||
|
|
||||||
|
|
@ -80,31 +80,44 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
||||||
[sharedHandleDownload, onDownloadComplete]
|
[sharedHandleDownload, onDownloadComplete]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (downloading && downloadingItemRef.current) {
|
||||||
|
downloadingItemRef.current.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'center',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [downloading]);
|
||||||
|
|
||||||
const renderROCmCard = () => {
|
const renderROCmCard = () => {
|
||||||
if (!rocmDownload) return null;
|
if (!rocmDownload) return null;
|
||||||
|
|
||||||
|
const isDownloading = Boolean(downloading) && downloadingType === 'rocm';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DownloadCard
|
<div ref={isDownloading ? downloadingItemRef : null}>
|
||||||
name={rocmDownload.name}
|
<DownloadCard
|
||||||
size={`~${formatFileSizeInMB(rocmDownload.size)}`}
|
name={rocmDownload.name}
|
||||||
description={getAssetDescription(rocmDownload.name)}
|
size={formatDownloadSize(rocmDownload.size, rocmDownload.url, true)}
|
||||||
version={rocmDownload.version}
|
description={getAssetDescription(rocmDownload.name)}
|
||||||
isRecommended={isAssetRecommended(
|
version={rocmDownload.version}
|
||||||
rocmDownload.name,
|
isRecommended={isAssetRecommended(
|
||||||
platformInfo.hasAMDGPU
|
rocmDownload.name,
|
||||||
)}
|
platformInfo.hasAMDGPU
|
||||||
isDownloading={Boolean(downloading) && downloadingType === 'rocm'}
|
)}
|
||||||
downloadProgress={
|
isDownloading={isDownloading}
|
||||||
downloadingType === 'rocm'
|
downloadProgress={
|
||||||
? downloadProgress[rocmDownload.name] || 0
|
downloadingType === 'rocm'
|
||||||
: 0
|
? downloadProgress[rocmDownload.name] || 0
|
||||||
}
|
: 0
|
||||||
disabled={Boolean(downloading) && downloadingType !== 'rocm'}
|
}
|
||||||
onDownload={(e) => {
|
disabled={Boolean(downloading) && downloadingType !== 'rocm'}
|
||||||
e.stopPropagation();
|
onDownload={(e) => {
|
||||||
handleDownload('rocm', rocmDownload);
|
e.stopPropagation();
|
||||||
}}
|
handleDownload('rocm', rocmDownload);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -126,40 +139,42 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
||||||
<>
|
<>
|
||||||
{availableDownloads.length > 0 ? (
|
{availableDownloads.length > 0 ? (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
{platformInfo.hasAMDGPU && !platformInfo.hasROCm && (
|
{platformInfo.hasAMDGPU &&
|
||||||
<Card withBorder p="md" bg="orange.0">
|
!platformInfo.hasROCm &&
|
||||||
<Stack gap="xs">
|
platformInfo.platform === 'linux' && (
|
||||||
<div
|
<Card withBorder p="md" bg="orange.0">
|
||||||
style={{
|
<Stack gap="xs">
|
||||||
display: 'flex',
|
<div
|
||||||
alignItems: 'center',
|
style={{
|
||||||
gap: '8px',
|
display: 'flex',
|
||||||
}}
|
alignItems: 'center',
|
||||||
>
|
gap: '8px',
|
||||||
<Text fw={600} c="orange.9">
|
}}
|
||||||
AMD GPU Detected
|
|
||||||
</Text>
|
|
||||||
<StyledTooltip
|
|
||||||
label="ROCm is not installed. Install ROCm for optimal AMD GPU performance with KoboldCpp."
|
|
||||||
multiline
|
|
||||||
w={220}
|
|
||||||
>
|
>
|
||||||
<Badge
|
<Text fw={600} c="orange.9">
|
||||||
|
AMD GPU Detected
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<Text size="sm" c="orange.8">
|
||||||
|
For best performance with your AMD GPU,
|
||||||
|
consider installing ROCm support.{' '}
|
||||||
|
<Anchor
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
window.electronAPI.app.openExternal(
|
||||||
|
'https://rocm.docs.amd.com/projects/install-on-linux/en/latest/reference/system-requirements.html'
|
||||||
|
);
|
||||||
|
}}
|
||||||
size="sm"
|
size="sm"
|
||||||
color="orange"
|
c="orange.8"
|
||||||
variant="filled"
|
|
||||||
>
|
>
|
||||||
ROCm Not Found
|
Learn more
|
||||||
</Badge>
|
</Anchor>
|
||||||
</StyledTooltip>
|
</Text>
|
||||||
</div>
|
</Stack>
|
||||||
<Text size="sm" c="orange.8">
|
</Card>
|
||||||
For best performance with your AMD GPU, consider
|
)}
|
||||||
installing ROCm support.
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{rocmDownload &&
|
{rocmDownload &&
|
||||||
platformInfo.hasAMDGPU &&
|
platformInfo.hasAMDGPU &&
|
||||||
|
|
@ -168,39 +183,47 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
||||||
{sortAssetsByRecommendation(
|
{sortAssetsByRecommendation(
|
||||||
regularDownloads,
|
regularDownloads,
|
||||||
platformInfo.hasAMDGPU
|
platformInfo.hasAMDGPU
|
||||||
).map((download) => (
|
).map((download) => {
|
||||||
<DownloadCard
|
const isDownloading =
|
||||||
key={download.name}
|
Boolean(downloading) &&
|
||||||
name={download.name}
|
downloadingType === 'asset' &&
|
||||||
size={formatFileSizeInMB(download.size)}
|
downloadingAsset === download.name;
|
||||||
version={download.version}
|
|
||||||
description={getAssetDescription(download.name)}
|
return (
|
||||||
isRecommended={isAssetRecommended(
|
<div
|
||||||
download.name,
|
key={download.name}
|
||||||
platformInfo.hasAMDGPU
|
ref={isDownloading ? downloadingItemRef : null}
|
||||||
)}
|
>
|
||||||
isDownloading={
|
<DownloadCard
|
||||||
Boolean(downloading) &&
|
name={download.name}
|
||||||
downloadingType === 'asset' &&
|
size={formatDownloadSize(
|
||||||
downloadingAsset === download.name
|
download.size,
|
||||||
}
|
download.url
|
||||||
downloadProgress={
|
)}
|
||||||
Boolean(downloading) &&
|
version={download.version}
|
||||||
downloadingType === 'asset' &&
|
description={getAssetDescription(download.name)}
|
||||||
downloadingAsset === download.name
|
isRecommended={isAssetRecommended(
|
||||||
? downloadProgress[download.name] || 0
|
download.name,
|
||||||
: 0
|
platformInfo.hasAMDGPU
|
||||||
}
|
)}
|
||||||
disabled={
|
isDownloading={isDownloading}
|
||||||
Boolean(downloading) &&
|
downloadProgress={
|
||||||
downloadingAsset !== download.name
|
isDownloading
|
||||||
}
|
? downloadProgress[download.name] || 0
|
||||||
onDownload={(e) => {
|
: 0
|
||||||
e.stopPropagation();
|
}
|
||||||
handleDownload('asset', download);
|
disabled={
|
||||||
}}
|
Boolean(downloading) &&
|
||||||
/>
|
downloadingAsset !== download.name
|
||||||
))}
|
}
|
||||||
|
onDownload={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleDownload('asset', download);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
{rocmDownload &&
|
{rocmDownload &&
|
||||||
!platformInfo.hasAMDGPU &&
|
!platformInfo.hasAMDGPU &&
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
|
|
@ -17,35 +17,15 @@ import {
|
||||||
sortAssetsByRecommendation,
|
sortAssetsByRecommendation,
|
||||||
isAssetRecommended,
|
isAssetRecommended,
|
||||||
} from '@/utils/assets';
|
} from '@/utils/assets';
|
||||||
import { getDisplayNameFromPath, formatFileSizeInMB } from '@/utils';
|
import {
|
||||||
|
getDisplayNameFromPath,
|
||||||
|
formatDownloadSize,
|
||||||
|
stripAssetExtensions,
|
||||||
|
compareVersions,
|
||||||
|
} from '@/utils';
|
||||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||||
import type { InstalledVersion, ReleaseWithStatus } from '@/types/electron';
|
import type { InstalledVersion, ReleaseWithStatus } from '@/types/electron';
|
||||||
|
|
||||||
const compareVersions = (versionA: string, versionB: string): number => {
|
|
||||||
const cleanVersion = (version: string): string =>
|
|
||||||
version.replace(/^v/, '').replace(/[^0-9.]/g, '');
|
|
||||||
|
|
||||||
const parseVersion = (version: string): number[] =>
|
|
||||||
cleanVersion(version)
|
|
||||||
.split('.')
|
|
||||||
.map((num) => parseInt(num, 10) || 0);
|
|
||||||
|
|
||||||
const a = parseVersion(versionA);
|
|
||||||
const b = parseVersion(versionB);
|
|
||||||
const maxLength = Math.max(a.length, b.length);
|
|
||||||
|
|
||||||
for (let i = 0; i < maxLength; i++) {
|
|
||||||
const aVal = a[i] || 0;
|
|
||||||
const bVal = b[i] || 0;
|
|
||||||
|
|
||||||
if (aVal !== bVal) {
|
|
||||||
return aVal - bVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface VersionInfo {
|
interface VersionInfo {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
|
|
@ -81,6 +61,7 @@ export const VersionsTab = () => {
|
||||||
const [latestRelease, setLatestRelease] = useState<ReleaseWithStatus | null>(
|
const [latestRelease, setLatestRelease] = useState<ReleaseWithStatus | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
const downloadingItemRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const loadInstalledVersions = useCallback(async () => {
|
const loadInstalledVersions = useCallback(async () => {
|
||||||
setLoadingInstalled(true);
|
setLoadingInstalled(true);
|
||||||
|
|
@ -146,14 +127,12 @@ export const VersionsTab = () => {
|
||||||
loadLatestRelease();
|
loadLatestRelease();
|
||||||
}, [loadInstalledVersions, loadLatestRelease]);
|
}, [loadInstalledVersions, loadLatestRelease]);
|
||||||
|
|
||||||
const getAllVersions = (): VersionInfo[] => {
|
const allVersions = useMemo((): VersionInfo[] => {
|
||||||
const versions: VersionInfo[] = [];
|
const versions: VersionInfo[] = [];
|
||||||
const processedInstalled = new Set<string>();
|
const processedInstalled = new Set<string>();
|
||||||
|
|
||||||
availableDownloads.forEach((download) => {
|
availableDownloads.forEach((download) => {
|
||||||
const downloadBaseName = download.name
|
const downloadBaseName = stripAssetExtensions(download.name);
|
||||||
.replace(/\.(tar\.gz|zip|exe)$/i, '')
|
|
||||||
.replace(/\.packed$/, '');
|
|
||||||
|
|
||||||
const installedVersion = installedVersions.find((v) => {
|
const installedVersion = installedVersions.find((v) => {
|
||||||
const displayName = getDisplayNameFromPath(v);
|
const displayName = getDisplayNameFromPath(v);
|
||||||
|
|
@ -220,7 +199,21 @@ export const VersionsTab = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return sortAssetsByRecommendation(versions, platformInfo.hasAMDGPU);
|
return sortAssetsByRecommendation(versions, platformInfo.hasAMDGPU);
|
||||||
};
|
}, [
|
||||||
|
availableDownloads,
|
||||||
|
installedVersions,
|
||||||
|
currentVersion,
|
||||||
|
platformInfo.hasAMDGPU,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (downloading && downloadingItemRef.current) {
|
||||||
|
downloadingItemRef.current.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'center',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [downloading]);
|
||||||
|
|
||||||
const handleDownload = async (version: VersionInfo) => {
|
const handleDownload = async (version: VersionInfo) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -362,21 +355,24 @@ export const VersionsTab = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
{getAllVersions().map((version, index) => {
|
{allVersions.map((version, index) => {
|
||||||
const isDownloading = downloading === version.name;
|
const isDownloading = downloading === version.name;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`${version.name}-${version.version}-${index}`}
|
key={`${version.name}-${version.version}-${index}`}
|
||||||
style={{ paddingBottom: '8px' }}
|
style={{ paddingBottom: '8px' }}
|
||||||
|
ref={isDownloading ? downloadingItemRef : null}
|
||||||
>
|
>
|
||||||
<DownloadCard
|
<DownloadCard
|
||||||
name={version.name}
|
name={version.name}
|
||||||
size={
|
size={
|
||||||
version.size
|
version.size
|
||||||
? version.isROCm
|
? formatDownloadSize(
|
||||||
? `~${formatFileSizeInMB(version.size)}`
|
version.size,
|
||||||
: formatFileSizeInMB(version.size)
|
version.downloadUrl,
|
||||||
|
version.isROCm
|
||||||
|
)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
version={version.version}
|
version={version.version}
|
||||||
|
|
@ -418,7 +414,7 @@ export const VersionsTab = () => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{getAllVersions().length === 0 && (
|
{allVersions.length === 0 && (
|
||||||
<Card withBorder radius="md" padding="md">
|
<Card withBorder radius="md" padding="md">
|
||||||
<Text size="sm" c="dimmed" ta="center">
|
<Text size="sm" c="dimmed" ta="center">
|
||||||
No versions found
|
No versions found
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,11 @@ export const ASSET_SUFFIXES = {
|
||||||
export const KOBOLDAI_URLS = {
|
export const KOBOLDAI_URLS = {
|
||||||
STANDARD_DOWNLOAD: 'https://koboldai.org/cpp',
|
STANDARD_DOWNLOAD: 'https://koboldai.org/cpp',
|
||||||
ROCM_DOWNLOAD: 'https://koboldai.org/cpplinuxrocm',
|
ROCM_DOWNLOAD: 'https://koboldai.org/cpplinuxrocm',
|
||||||
|
DOMAIN: 'koboldai.org',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const GITHUB_URLS = {
|
||||||
|
DOMAIN: 'github.com',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const ROCM = {
|
export const ROCM = {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable no-comments/disallowComments */
|
||||||
import { spawn, ChildProcess } from 'child_process';
|
import { spawn, ChildProcess } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import {
|
import {
|
||||||
|
|
@ -16,7 +17,8 @@ import { GitHubService } from '@/main/services/GitHubService';
|
||||||
import { ConfigManager } from '@/main/managers/ConfigManager';
|
import { ConfigManager } from '@/main/managers/ConfigManager';
|
||||||
import { LogManager } from '@/main/managers/LogManager';
|
import { LogManager } from '@/main/managers/LogManager';
|
||||||
import { WindowManager } from '@/main/managers/WindowManager';
|
import { WindowManager } from '@/main/managers/WindowManager';
|
||||||
import { ROCM, GITHUB_API } from '@/constants';
|
import { ROCM } from '@/constants';
|
||||||
|
import { stripAssetExtensions } from '@/utils/versionUtils';
|
||||||
import type { DownloadItem } from '@/types/electron';
|
import type { DownloadItem } from '@/types/electron';
|
||||||
|
|
||||||
interface GitHubAsset {
|
interface GitHubAsset {
|
||||||
|
|
@ -85,7 +87,7 @@ export class KoboldCppManager {
|
||||||
onProgress?: (progress: number) => void
|
onProgress?: (progress: number) => void
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const tempPackedFilePath = join(this.installDir, `${asset.name}.packed`);
|
const tempPackedFilePath = join(this.installDir, `${asset.name}.packed`);
|
||||||
const baseFilename = asset.name.replace(/\.exe$/, '');
|
const baseFilename = stripAssetExtensions(asset.name);
|
||||||
const unpackedDirPath = join(this.installDir, baseFilename);
|
const unpackedDirPath = join(this.installDir, baseFilename);
|
||||||
|
|
||||||
if (asset.isUpdate && existsSync(unpackedDirPath)) {
|
if (asset.isUpdate && existsSync(unpackedDirPath)) {
|
||||||
|
|
@ -666,32 +668,35 @@ export class KoboldCppManager {
|
||||||
type: 'rocm',
|
type: 'rocm',
|
||||||
};
|
};
|
||||||
} else if (platform === 'win32') {
|
} else if (platform === 'win32') {
|
||||||
try {
|
return null;
|
||||||
const response = await fetch(GITHUB_API.ROCM_LATEST_RELEASE_URL);
|
// The launcher doesn't exist in unpacked state yet.
|
||||||
if (!response.ok) {
|
// Enable when it's ready.
|
||||||
return null;
|
// try {
|
||||||
}
|
// const response = await fetch(GITHUB_API.ROCM_LATEST_RELEASE_URL);
|
||||||
|
// if (!response.ok) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
const release = await response.json();
|
// const release = await response.json();
|
||||||
const rocmAsset = release.assets?.find((asset: GitHubAsset) =>
|
// const rocmAsset = release.assets?.find((asset: GitHubAsset) =>
|
||||||
asset.name.endsWith('rocm.exe')
|
// asset.name.endsWith('rocm.exe')
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (rocmAsset) {
|
// if (rocmAsset) {
|
||||||
return {
|
// return {
|
||||||
name: rocmAsset.name,
|
// name: rocmAsset.name,
|
||||||
url: rocmAsset.browser_download_url,
|
// url: rocmAsset.browser_download_url,
|
||||||
size: rocmAsset.size,
|
// size: rocmAsset.size,
|
||||||
version: release.tag_name?.replace(/^v/, '') || 'unknown',
|
// version: release.tag_name?.replace(/^v/, '') || 'unknown',
|
||||||
type: 'rocm',
|
// type: 'rocm',
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
this.logManager.logError(
|
// this.logManager.logError(
|
||||||
'Failed to fetch Windows ROCm release:',
|
// 'Failed to fetch Windows ROCm release:',
|
||||||
error as Error
|
// error as Error
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -715,7 +720,7 @@ export class KoboldCppManager {
|
||||||
this.installDir,
|
this.installDir,
|
||||||
`${rocmInfo.name}.packed`
|
`${rocmInfo.name}.packed`
|
||||||
);
|
);
|
||||||
const baseFilename = rocmInfo.name.replace(/\.exe$/, '');
|
const baseFilename = stripAssetExtensions(rocmInfo.name);
|
||||||
const unpackedDirPath = join(this.installDir, baseFilename);
|
const unpackedDirPath = join(this.installDir, baseFilename);
|
||||||
|
|
||||||
const response = await fetch(rocmInfo.url);
|
const response = await fetch(rocmInfo.url);
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ export class WindowManager {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Open Logs Directory',
|
label: 'View Error Logs',
|
||||||
click: () => {
|
click: () => {
|
||||||
const logsDir = join(app.getPath('userData'), 'logs');
|
const logsDir = join(app.getPath('userData'), 'logs');
|
||||||
shell.openPath(logsDir);
|
shell.openPath(logsDir);
|
||||||
|
|
|
||||||
|
|
@ -37,21 +37,18 @@ export class BinaryService {
|
||||||
const binaryDir = dirname(koboldBinaryPath);
|
const binaryDir = dirname(koboldBinaryPath);
|
||||||
const internalDir = join(binaryDir, '_internal');
|
const internalDir = join(binaryDir, '_internal');
|
||||||
|
|
||||||
if (!existsSync(internalDir)) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.warn(
|
|
||||||
'_internal directory not found, cannot detect backend support'
|
|
||||||
);
|
|
||||||
this.backendSupportCache.set(koboldBinaryPath, support);
|
|
||||||
return support;
|
|
||||||
}
|
|
||||||
|
|
||||||
const platform = process.platform;
|
const platform = process.platform;
|
||||||
const isDynamicLib = (name: string) => {
|
const isDynamicLib = (name: string) => {
|
||||||
if (platform === 'win32') {
|
if (platform === 'win32') {
|
||||||
return existsSync(join(internalDir, `${name}.dll`));
|
return (
|
||||||
|
existsSync(join(internalDir, `${name}.dll`)) ||
|
||||||
|
existsSync(join(binaryDir, `${name}.dll`))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return existsSync(join(internalDir, `${name}.so`));
|
return (
|
||||||
|
existsSync(join(internalDir, `${name}.so`)) ||
|
||||||
|
existsSync(join(binaryDir, `${name}.so`))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { ASSET_SUFFIXES } from '@/constants';
|
import { ASSET_SUFFIXES } from '@/constants';
|
||||||
|
import { stripAssetExtensions } from '@/utils/versionUtils';
|
||||||
|
|
||||||
export const getAssetDescription = (assetName: string): string => {
|
export const getAssetDescription = (assetName: string): string => {
|
||||||
const name = assetName.toLowerCase();
|
const name = stripAssetExtensions(assetName).toLowerCase();
|
||||||
|
|
||||||
if (name.includes(ASSET_SUFFIXES.ROCM)) {
|
if (name.includes(ASSET_SUFFIXES.ROCM)) {
|
||||||
return 'Optimized for AMD GPUs with ROCm support.';
|
return 'Optimized for AMD GPUs with ROCm support.';
|
||||||
|
|
@ -22,7 +23,7 @@ export const isAssetRecommended = (
|
||||||
assetName: string,
|
assetName: string,
|
||||||
hasAMDGPU: boolean
|
hasAMDGPU: boolean
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const name = assetName.toLowerCase();
|
const name = stripAssetExtensions(assetName).toLowerCase();
|
||||||
|
|
||||||
if (hasAMDGPU && name.includes(ASSET_SUFFIXES.ROCM)) {
|
if (hasAMDGPU && name.includes(ASSET_SUFFIXES.ROCM)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -36,6 +37,16 @@ export const isAssetRecommended = (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isAssetStandard = (assetName: string): boolean => {
|
||||||
|
const name = stripAssetExtensions(assetName).toLowerCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
!name.includes(ASSET_SUFFIXES.ROCM) &&
|
||||||
|
!name.endsWith(ASSET_SUFFIXES.OLDPC) &&
|
||||||
|
!name.endsWith(ASSET_SUFFIXES.NOCUDA)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const sortAssetsByRecommendation = <T extends { name: string }>(
|
export const sortAssetsByRecommendation = <T extends { name: string }>(
|
||||||
assets: T[],
|
assets: T[],
|
||||||
hasAMDGPU: boolean
|
hasAMDGPU: boolean
|
||||||
|
|
@ -43,9 +54,16 @@ export const sortAssetsByRecommendation = <T extends { name: string }>(
|
||||||
[...assets].sort((a, b) => {
|
[...assets].sort((a, b) => {
|
||||||
const aRecommended = isAssetRecommended(a.name, hasAMDGPU);
|
const aRecommended = isAssetRecommended(a.name, hasAMDGPU);
|
||||||
const bRecommended = isAssetRecommended(b.name, hasAMDGPU);
|
const bRecommended = isAssetRecommended(b.name, hasAMDGPU);
|
||||||
|
const aStandard = isAssetStandard(a.name);
|
||||||
|
const bStandard = isAssetStandard(b.name);
|
||||||
|
|
||||||
if (aRecommended && !bRecommended) return -1;
|
if (aRecommended && !bRecommended) return -1;
|
||||||
if (!aRecommended && bRecommended) return 1;
|
if (!aRecommended && bRecommended) return 1;
|
||||||
|
|
||||||
|
if (aRecommended === bRecommended) {
|
||||||
|
if (aStandard && !bStandard) return -1;
|
||||||
|
if (!aStandard && bStandard) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
43
src/utils/downloadUtils.ts
Normal file
43
src/utils/downloadUtils.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { formatFileSizeInMB } from '@/utils/fileSize';
|
||||||
|
import { KOBOLDAI_URLS, GITHUB_URLS } from '@/constants';
|
||||||
|
|
||||||
|
export const formatDownloadSize = (
|
||||||
|
size: number,
|
||||||
|
url?: string,
|
||||||
|
isROCm?: boolean
|
||||||
|
): string => {
|
||||||
|
if (!size) return '';
|
||||||
|
|
||||||
|
const isApproximateSize =
|
||||||
|
url?.includes(KOBOLDAI_URLS.DOMAIN) ||
|
||||||
|
(isROCm && !url?.includes(GITHUB_URLS.DOMAIN));
|
||||||
|
|
||||||
|
return isApproximateSize
|
||||||
|
? `~${formatFileSizeInMB(size)}`
|
||||||
|
: formatFileSizeInMB(size);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const compareVersions = (versionA: string, versionB: string): number => {
|
||||||
|
const cleanVersion = (version: string): string =>
|
||||||
|
version.replace(/^v/, '').replace(/[^0-9.]/g, '');
|
||||||
|
|
||||||
|
const parseVersion = (version: string): number[] =>
|
||||||
|
cleanVersion(version)
|
||||||
|
.split('.')
|
||||||
|
.map((num) => parseInt(num, 10) || 0);
|
||||||
|
|
||||||
|
const a = parseVersion(versionA);
|
||||||
|
const b = parseVersion(versionB);
|
||||||
|
const maxLength = Math.max(a.length, b.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < maxLength; i++) {
|
||||||
|
const aVal = a[i] || 0;
|
||||||
|
const bVal = b[i] || 0;
|
||||||
|
|
||||||
|
if (aVal !== bVal) {
|
||||||
|
return aVal - bVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './assets';
|
export * from './assets';
|
||||||
|
export * from './downloadUtils';
|
||||||
export * from './fileSize';
|
export * from './fileSize';
|
||||||
export * from './imageModelPresets';
|
export * from './imageModelPresets';
|
||||||
export * from './nullish';
|
export * from './nullish';
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,8 @@ export const getDisplayNameFromPath = (
|
||||||
|
|
||||||
return installedVersion.filename;
|
return installedVersion.filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const stripAssetExtensions = (assetName: string): string =>
|
||||||
|
assetName
|
||||||
|
.replace(/\.(tar\.gz|zip|exe|dmg|AppImage)$/i, '')
|
||||||
|
.replace(/\.packed$/, '');
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue