don't need to recommend ROCm as it's not that good compared to generally available Vulkan

This commit is contained in:
lone-cloud 2025-08-20 10:48:48 -07:00
parent 1e7fd585f7
commit 82aa274e00
8 changed files with 42 additions and 208 deletions

View file

@ -9,7 +9,6 @@ A koboldcpp manager. <!-- markdownlint-disable MD033 -->
- modern UI for [koboldcpp](https://github.com/LostRuins/koboldcpp) with full support for Linux Wayland
- download and keep up-to-date your [koboldcpp](https://github.com/LostRuins/koboldcpp/releases) binary
- better surface the ROCm-specific builds of koboldcpp from YellowRoseCx and from [koboldai.org](https://koboldai.org/cpplinuxrocm)
- manage the koboldcpp binary to prevent it from running in the background indefinitely
- automatically unpack all downloaded koboldcpp binaries for significantly faster operation and reduced RAM+HDD utilization (up to ~4GB less RAM usage for ROCm)
- added presets for a basic flux or chroma image generation setup

View file

@ -113,13 +113,7 @@ export const App = () => {
const handleBinaryUpdate = async (download: DownloadItem) => {
try {
const downloadType = download.type === 'rocm' ? 'rocm' : 'asset';
const success = await sharedHandleDownload(
downloadType,
download,
true,
true
);
const success = await sharedHandleDownload('asset', download, true, true);
if (success) {
dismissUpdate();

View file

@ -18,7 +18,6 @@ interface DownloadCardProps {
size: string;
version?: string;
description?: string;
isRecommended?: boolean;
isCurrent?: boolean;
isInstalled?: boolean;
isDownloading?: boolean;
@ -36,7 +35,6 @@ export const DownloadCard = ({
size,
version,
description,
isRecommended = false,
isCurrent = false,
isInstalled = false,
isDownloading = false,
@ -122,11 +120,6 @@ export const DownloadCard = ({
Current
</Badge>
)}
{isRecommended && (
<Badge variant="light" color="blue" size="sm">
Recommended
</Badge>
)}
{hasUpdate && (
<Badge variant="light" color="orange" size="sm">
Update Available

View file

@ -1,20 +1,8 @@
import { useState, useCallback, useEffect, useRef } from 'react';
import {
Card,
Text,
Title,
Loader,
Stack,
Container,
Anchor,
} from '@mantine/core';
import { Card, Text, Title, Loader, Stack, Container } from '@mantine/core';
import { DownloadCard } from '@/components/DownloadCard';
import { getPlatformDisplayName, formatDownloadSize } from '@/utils';
import {
isAssetRecommended,
sortAssetsByRecommendation,
getAssetDescription,
} from '@/utils/assets';
import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
import type { DownloadItem } from '@/types/electron';
@ -33,27 +21,20 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
handleDownload: sharedHandleDownload,
} = useKoboldVersions();
const [downloadingType, setDownloadingType] = useState<
'asset' | 'rocm' | null
>(null);
const [downloadingAsset, setDownloadingAsset] = useState<string | null>(null);
const downloadingItemRef = useRef<HTMLDivElement>(null);
const loading = loadingPlatform || loadingRemote;
const regularDownloads = availableDownloads.filter((d) => d.type === 'asset');
const rocmDownload = availableDownloads.find((d) => d.type === 'rocm');
const sortedDownloads = sortDownloadsByType(availableDownloads);
const handleDownload = useCallback(
async (type: 'asset' | 'rocm', download?: DownloadItem) => {
if (type === 'asset' && !download) return;
setDownloadingType(type);
setDownloadingAsset(type === 'asset' ? download!.name : null);
async (download: DownloadItem) => {
setDownloadingAsset(download.name);
try {
const success = await sharedHandleDownload(
type,
'asset',
download,
false,
false
@ -63,17 +44,15 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
onDownloadComplete();
setTimeout(() => {
setDownloadingType(null);
setDownloadingAsset(null);
}, 200);
}
} catch (error) {
window.electronAPI.logs.logError(
`Failed to download ${type}:`,
`Failed to download ${download.name}:`,
error as Error
);
} finally {
setDownloadingType(null);
setDownloadingAsset(null);
}
},
@ -89,38 +68,6 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
}
}, [downloading]);
const renderROCmCard = () => {
if (!rocmDownload) return null;
const isDownloading = Boolean(downloading) && downloadingType === 'rocm';
return (
<div ref={isDownloading ? downloadingItemRef : null}>
<DownloadCard
name={rocmDownload.name}
size={formatDownloadSize(rocmDownload.size, rocmDownload.url, true)}
description={getAssetDescription(rocmDownload.name)}
version={rocmDownload.version}
isRecommended={isAssetRecommended(
rocmDownload.name,
platformInfo.hasAMDGPU
)}
isDownloading={isDownloading}
downloadProgress={
downloadingType === 'rocm'
? downloadProgress[rocmDownload.name] || 0
: 0
}
disabled={Boolean(downloading) && downloadingType !== 'rocm'}
onDownload={(e) => {
e.stopPropagation();
handleDownload('rocm', rocmDownload);
}}
/>
</div>
);
};
return (
<Container size="sm">
<Stack gap="xl">
@ -139,54 +86,9 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
<>
{availableDownloads.length > 0 ? (
<Stack gap="sm">
{platformInfo.hasAMDGPU &&
!platformInfo.hasROCm &&
platformInfo.platform === 'linux' && (
<Card withBorder p="md" bg="orange.0">
<Stack gap="xs">
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
}}
>
<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"
c="orange.8"
>
Learn more
</Anchor>
</Text>
</Stack>
</Card>
)}
{rocmDownload &&
platformInfo.hasAMDGPU &&
renderROCmCard()}
{sortAssetsByRecommendation(
regularDownloads,
platformInfo.hasAMDGPU
).map((download) => {
{sortedDownloads.map((download) => {
const isDownloading =
Boolean(downloading) &&
downloadingType === 'asset' &&
downloadingAsset === download.name;
return (
@ -202,10 +104,6 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
)}
version={download.version}
description={getAssetDescription(download.name)}
isRecommended={isAssetRecommended(
download.name,
platformInfo.hasAMDGPU
)}
isDownloading={isDownloading}
downloadProgress={
isDownloading
@ -218,16 +116,12 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
}
onDownload={(e) => {
e.stopPropagation();
handleDownload('asset', download);
handleDownload(download);
}}
/>
</div>
);
})}
{rocmDownload &&
!platformInfo.hasAMDGPU &&
renderROCmCard()}
</Stack>
) : (
<Card withBorder p="md" bg="yellow.0" c="yellow.9">

View file

@ -12,11 +12,7 @@ import {
} from '@mantine/core';
import { RotateCcw, ExternalLink } from 'lucide-react';
import { DownloadCard } from '@/components/DownloadCard';
import {
getAssetDescription,
sortAssetsByRecommendation,
isAssetRecommended,
} from '@/utils/assets';
import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
import {
getDisplayNameFromPath,
formatDownloadSize,
@ -34,14 +30,12 @@ interface VersionInfo {
isCurrent: boolean;
downloadUrl?: string;
installedPath?: string;
isROCm?: boolean;
hasUpdate?: boolean;
newerVersion?: string;
}
export const VersionsTab = () => {
const {
platformInfo,
availableDownloads,
loadingPlatform,
loadingRemote,
@ -131,7 +125,9 @@ export const VersionsTab = () => {
const versions: VersionInfo[] = [];
const processedInstalled = new Set<string>();
availableDownloads.forEach((download) => {
const sortedDownloads = sortDownloadsByType(availableDownloads);
sortedDownloads.forEach((download) => {
const downloadBaseName = stripAssetExtensions(download.name);
const installedVersion = installedVersions.find((v) => {
@ -162,7 +158,6 @@ export const VersionsTab = () => {
isCurrent,
downloadUrl: download.url,
installedPath: installedVersion.path,
isROCm: download.type === 'rocm',
hasUpdate,
newerVersion: hasUpdate ? download.version : undefined,
});
@ -174,7 +169,6 @@ export const VersionsTab = () => {
isInstalled: false,
isCurrent: false,
downloadUrl: download.url,
isROCm: download.type === 'rocm',
});
}
});
@ -193,18 +187,12 @@ export const VersionsTab = () => {
isInstalled: true,
isCurrent,
installedPath: installed.path,
isROCm: false,
});
}
});
return sortAssetsByRecommendation(versions, platformInfo.hasAMDGPU);
}, [
availableDownloads,
installedVersions,
currentVersion,
platformInfo.hasAMDGPU,
]);
return versions;
}, [availableDownloads, installedVersions, currentVersion]);
useEffect(() => {
if (downloading && downloadingItemRef.current) {
@ -222,9 +210,8 @@ export const VersionsTab = () => {
throw new Error('Download not found');
}
const downloadType = download.type === 'rocm' ? 'rocm' : 'asset';
const success = await sharedHandleDownload(
downloadType,
'asset',
download,
false,
false
@ -245,10 +232,9 @@ export const VersionsTab = () => {
throw new Error('Download not found');
}
const downloadType = download.type === 'rocm' ? 'rocm' : 'asset';
const wasCurrentBinary = version.isCurrent;
const success = await sharedHandleDownload(
downloadType,
'asset',
download,
true,
wasCurrentBinary
@ -368,19 +354,11 @@ export const VersionsTab = () => {
name={version.name}
size={
version.size
? formatDownloadSize(
version.size,
version.downloadUrl,
version.isROCm
)
? formatDownloadSize(version.size, version.downloadUrl)
: ''
}
version={version.version}
description={getAssetDescription(version.name)}
isRecommended={isAssetRecommended(
version.name,
platformInfo.hasAMDGPU
)}
isCurrent={version.isCurrent}
isInstalled={version.isInstalled}
isDownloading={isDownloading}

View file

@ -38,10 +38,6 @@ export const KOBOLDAI_URLS = {
DOMAIN: 'koboldai.org',
} as const;
export const GITHUB_URLS = {
DOMAIN: 'github.com',
} as const;
export const ROCM = {
BINARY_NAME: 'koboldcpp-linux-x64-rocm',
DOWNLOAD_URL: KOBOLDAI_URLS.ROCM_DOWNLOAD,

View file

@ -5,11 +5,11 @@ export const getAssetDescription = (assetName: string): string => {
const name = stripAssetExtensions(assetName).toLowerCase();
if (name.includes(ASSET_SUFFIXES.ROCM)) {
return 'Optimized for AMD GPUs with ROCm support.';
return 'Adds dedicated ROCm support for AMD GPUs. Note that ROCm is unlikely to perform better than modern Vulkan in most cases.';
}
if (name.endsWith(ASSET_SUFFIXES.OLDPC)) {
return 'Meant for old PCs that cannot run the standard build.';
return 'Meant for old PCs with outdated CPUs that may not work with the standard build. Does not support modern AVX2 CPU architectures.';
}
if (name.endsWith(ASSET_SUFFIXES.NOCUDA)) {
@ -19,24 +19,6 @@ export const getAssetDescription = (assetName: string): string => {
return "Standard build that's ideal for most cases.";
};
export const isAssetRecommended = (
assetName: string,
hasAMDGPU: boolean
): boolean => {
const name = stripAssetExtensions(assetName).toLowerCase();
if (hasAMDGPU && name.includes(ASSET_SUFFIXES.ROCM)) {
return true;
}
return (
!hasAMDGPU &&
!name.includes(ASSET_SUFFIXES.ROCM) &&
!name.endsWith(ASSET_SUFFIXES.OLDPC) &&
!name.endsWith(ASSET_SUFFIXES.NOCUDA)
);
};
export const isAssetStandard = (assetName: string): boolean => {
const name = stripAssetExtensions(assetName).toLowerCase();
@ -47,23 +29,27 @@ export const isAssetStandard = (assetName: string): boolean => {
);
};
export const sortAssetsByRecommendation = <T extends { name: string }>(
assets: T[],
hasAMDGPU: boolean
export const sortDownloadsByType = <T extends { name: string }>(
downloads: T[]
): T[] =>
[...assets].sort((a, b) => {
const aRecommended = isAssetRecommended(a.name, hasAMDGPU);
const bRecommended = isAssetRecommended(b.name, hasAMDGPU);
const aStandard = isAssetStandard(a.name);
const bStandard = isAssetStandard(b.name);
[...downloads].sort((a, b) => {
const aName = stripAssetExtensions(a.name).toLowerCase();
const bName = stripAssetExtensions(b.name).toLowerCase();
if (aRecommended && !bRecommended) return -1;
if (!aRecommended && bRecommended) return 1;
const getOrderPriority = (name: string): number => {
if (isAssetStandard(name)) return 0;
if (name.includes(ASSET_SUFFIXES.ROCM)) return 1;
if (name.endsWith(ASSET_SUFFIXES.NOCUDA)) return 2;
if (name.endsWith(ASSET_SUFFIXES.OLDPC)) return 3;
return 4;
};
if (aRecommended === bRecommended) {
if (aStandard && !bStandard) return -1;
if (!aStandard && bStandard) return 1;
const aPriority = getOrderPriority(aName);
const bPriority = getOrderPriority(bName);
if (aPriority !== bPriority) {
return aPriority - bPriority;
}
return 0;
return a.name.localeCompare(b.name);
});

View file

@ -1,16 +1,10 @@
import { formatFileSizeInMB } from '@/utils/fileSize';
import { KOBOLDAI_URLS, GITHUB_URLS } from '@/constants';
import { KOBOLDAI_URLS } from '@/constants';
export const formatDownloadSize = (
size: number,
url?: string,
isROCm?: boolean
): string => {
export const formatDownloadSize = (size: number, url?: string): string => {
if (!size) return '';
const isApproximateSize =
url?.includes(KOBOLDAI_URLS.DOMAIN) ||
(isROCm && !url?.includes(GITHUB_URLS.DOMAIN));
const isApproximateSize = url?.includes(KOBOLDAI_URLS.DOMAIN);
return isApproximateSize
? `~${formatFileSizeInMB(size)}`