diff --git a/README.md b/README.md index 33792f7..275d0ac 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ A koboldcpp manager. - 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 diff --git a/src/App.tsx b/src/App.tsx index 29cb450..25988ad 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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(); diff --git a/src/components/DownloadCard.tsx b/src/components/DownloadCard.tsx index 915bbce..1b4d036 100644 --- a/src/components/DownloadCard.tsx +++ b/src/components/DownloadCard.tsx @@ -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 )} - {isRecommended && ( - - Recommended - - )} {hasUpdate && ( Update Available diff --git a/src/components/screens/Download.tsx b/src/components/screens/Download.tsx index c36b62b..0bc4f37 100644 --- a/src/components/screens/Download.tsx +++ b/src/components/screens/Download.tsx @@ -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(null); const downloadingItemRef = useRef(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 ( -
- { - e.stopPropagation(); - handleDownload('rocm', rocmDownload); - }} - /> -
- ); - }; - return ( @@ -139,54 +86,9 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => { <> {availableDownloads.length > 0 ? ( - {platformInfo.hasAMDGPU && - !platformInfo.hasROCm && - platformInfo.platform === 'linux' && ( - - -
- - AMD GPU Detected - -
- - For best performance with your AMD GPU, - consider installing ROCm support.{' '} - { - 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 - - -
-
- )} - - {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); }} /> ); })} - - {rocmDownload && - !platformInfo.hasAMDGPU && - renderROCmCard()}
) : ( diff --git a/src/components/settings/VersionsTab.tsx b/src/components/settings/VersionsTab.tsx index 387cfef..ff087a1 100644 --- a/src/components/settings/VersionsTab.tsx +++ b/src/components/settings/VersionsTab.tsx @@ -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(); - 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} diff --git a/src/constants/index.ts b/src/constants/index.ts index 4927ca0..b3e42bf 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -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, diff --git a/src/utils/assets.ts b/src/utils/assets.ts index e3fc74e..85a547a 100644 --- a/src/utils/assets.ts +++ b/src/utils/assets.ts @@ -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 = ( - assets: T[], - hasAMDGPU: boolean +export const sortDownloadsByType = ( + 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); }); diff --git a/src/utils/downloadUtils.ts b/src/utils/downloadUtils.ts index 58ab666..e0a521a 100644 --- a/src/utils/downloadUtils.ts +++ b/src/utils/downloadUtils.ts @@ -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)}`