diff --git a/src-tauri/src/core/hardware/mod.rs b/src-tauri/src/core/hardware/mod.rs index d1bd41d38..ea2435cb0 100644 --- a/src-tauri/src/core/hardware/mod.rs +++ b/src-tauri/src/core/hardware/mod.rs @@ -24,7 +24,14 @@ impl CpuStaticInfo { let name = system .cpus() .first() - .map(|cpu| cpu.brand()) + .map(|cpu| { + let brand = cpu.brand(); + if brand.is_empty() { + cpu.name() + } else { + brand + } + }) .unwrap_or("unknown") .to_string(); diff --git a/web-app/src/hooks/useHardware.ts b/web-app/src/hooks/useHardware.ts index 16e83a7a5..3d2e59c79 100644 --- a/web-app/src/hooks/useHardware.ts +++ b/web-app/src/hooks/useHardware.ts @@ -6,9 +6,9 @@ import { setActiveGpus } from '@/services/hardware' // Hardware data types export interface CPU { arch: string - cores: number - instructions: string[] - model: string + core_count: number + extensions: string[] + name: string usage: number } @@ -18,14 +18,21 @@ export interface GPUAdditionalInfo { } export interface GPU { - activated: boolean - additional_information: GPUAdditionalInfo - free_vram: number - id: string name: string - total_vram: number + total_memory: number + vendor: string uuid: string - version: string + driver_version: string + nvidia_info: { + index: number + compute_capability: string + } + vulkan_info: { + index: number + device_id: number + device_type: string + api_version: string + } } export interface OS { @@ -41,33 +48,48 @@ export interface RAM { export interface HardwareData { cpu: CPU gpus: GPU[] - os: OS - ram: RAM + os_type: string + os_name: string + total_memory: number +} + +export interface SystemUsage { + cpu: number + used_memory: number + total_memory: number + gpus: { + uuid: string + used_memory: number + total_memory: number + }[] } // Default values const defaultHardwareData: HardwareData = { cpu: { arch: '', - cores: 0, - instructions: [], - model: '', + core_count: 0, + extensions: [], + name: '', usage: 0, }, gpus: [], - os: { - name: '', - version: '', - }, - ram: { - available: 0, - total: 0, - }, + os_type: '', + os_name: '', + total_memory: 0, +} + +const defaultSystemUsage: SystemUsage = { + cpu: 0, + used_memory: 0, + total_memory: 0, + gpus: [], } interface HardwareStore { // Hardware data hardwareData: HardwareData + systemUsage: SystemUsage // Update functions setCPU: (cpu: CPU) => void @@ -81,11 +103,8 @@ interface HardwareStore { // Update individual GPU updateGPU: (index: number, gpu: GPU) => void - // Update CPU usage - updateCPUUsage: (usage: number) => void - // Update RAM available - updateRAMAvailable: (available: number) => void + updateSystemUsage: (usage: SystemUsage) => void // Toggle GPU activation (async, with loading) toggleGPUActivation: (index: number) => Promise @@ -107,11 +126,15 @@ export const useHardware = create()( persist( (set, get) => ({ hardwareData: defaultHardwareData, + systemUsage: defaultSystemUsage, gpuLoading: {}, pollingPaused: false, setGpuLoading: (index, loading) => set((state) => ({ - gpuLoading: { ...state.gpuLoading, [state.hardwareData.gpus[index].uuid]: loading }, + gpuLoading: { + ...state.gpuLoading, + [state.hardwareData.gpus[index].uuid]: loading, + }, })), pausePolling: () => set({ pollingPaused: true }), resumePolling: () => set({ pollingPaused: false }), @@ -167,56 +190,41 @@ export const useHardware = create()( } }), - updateCPUUsage: (usage) => - set((state) => ({ - hardwareData: { - ...state.hardwareData, - cpu: { - ...state.hardwareData.cpu, - usage, - }, - }, - })), - - updateRAMAvailable: (available) => - set((state) => ({ - hardwareData: { - ...state.hardwareData, - ram: { - ...state.hardwareData.ram, - available, - }, - }, + updateSystemUsage: (systemUsage) => + set(() => ({ + systemUsage, })), toggleGPUActivation: async (index) => { - const { pausePolling, setGpuLoading, resumePolling } = get(); - pausePolling(); - setGpuLoading(index, true); - try { - await new Promise((resolve) => setTimeout(resolve, 200)); // Simulate async, replace with real API if needed - set((state) => { - const newGPUs = [...state.hardwareData.gpus]; - if (index >= 0 && index < newGPUs.length) { - newGPUs[index] = { - ...newGPUs[index], - activated: !newGPUs[index].activated, - }; - } - setActiveGpus({ - gpus: newGPUs.filter((e) => e.activated).map((e) => parseInt(e.id)), - }); - return { - hardwareData: { - ...state.hardwareData, - gpus: newGPUs, - }, - }; - }); - } finally { - setGpuLoading(index, false); - setTimeout(resumePolling, 1000); // Resume polling after 1s - } + const { pausePolling, setGpuLoading, resumePolling } = get() + pausePolling() + setGpuLoading(index, true) + // try { + // await new Promise((resolve) => setTimeout(resolve, 200)) // Simulate async, replace with real API if needed + // set((state) => { + // const newGPUs = [...state.hardwareData.gpus] + // if (index >= 0 && index < newGPUs.length) { + // newGPUs[index] = { + // ...newGPUs[index], + // activated: !newGPUs[index].activated, + // } + // } + // setActiveGpus({ + // gpus: newGPUs + // .filter((e) => e.activated) + // .map((e) => parseInt(e.id)), + // }) + // return { + // hardwareData: { + // ...state.hardwareData, + // gpus: newGPUs, + // }, + // } + // }) + // } finally { + // setGpuLoading(index, false) + // setTimeout(resumePolling, 1000) // Resume polling after 1s + // } }, reorderGPUs: (oldIndex, newIndex) => diff --git a/web-app/src/routes/settings/hardware.tsx b/web-app/src/routes/settings/hardware.tsx index 33267c6a1..8134e5230 100644 --- a/web-app/src/routes/settings/hardware.tsx +++ b/web-app/src/routes/settings/hardware.tsx @@ -29,10 +29,11 @@ import { IconGripVertical, IconDeviceDesktopAnalytics, } from '@tabler/icons-react' -import { getHardwareInfo } from '@/services/hardware' +import { getHardwareInfo, getSystemUsage } from '@/services/hardware' import { WebviewWindow } from '@tauri-apps/api/webviewWindow' import { formatMegaBytes } from '@/lib/utils' import { windowKey } from '@/constants/windows' +import { toNumber } from '@/utils/number' // eslint-disable-next-line @typescript-eslint/no-explicit-any export const Route = createFileRoute(route.settings.hardware as any)({ @@ -47,10 +48,11 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) { transform, transition, isDragging, - } = useSortable({ id: gpu.id || index }) + } = useSortable({ id: index }) const { t } = useTranslation() - const { toggleGPUActivation, gpuLoading } = useHardware() + const { systemUsage, toggleGPUActivation, gpuLoading } = useHardware() + const usage = systemUsage.gpus[index] const style = { transform: CSS.Transform.toString(transform), @@ -78,7 +80,7 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) { actions={
toggleGPUActivation(index)} /> @@ -90,8 +92,9 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) { title={t('settings:hardware.vram')} actions={ - {formatMegaBytes(gpu.free_vram)} {t('settings:hardware.freeOf')}{' '} - {formatMegaBytes(gpu.total_vram)} + {formatMegaBytes(usage?.used_memory)}{' '} + {t('settings:hardware.freeOf')}{' '} + {formatMegaBytes(gpu.total_memory)} } /> @@ -99,7 +102,7 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) { title={t('settings:hardware.driverVersion')} actions={ - {gpu.additional_information?.driver_version || '-'} + {gpu.driver_version?.slice(0, 50) || '-'} } /> @@ -107,7 +110,8 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) { title={t('settings:hardware.computeCapability')} actions={ - {gpu.additional_information?.compute_cap || '-'} + {gpu.nvidia_info?.compute_capability ?? + gpu.vulkan_info?.api_version} } /> @@ -120,9 +124,9 @@ function Hardware() { const { t } = useTranslation() const { hardwareData, + systemUsage, setHardwareData, - updateCPUUsage, - updateRAMAvailable, + updateSystemUsage, reorderGPUs, pollingPaused, } = useHardware() @@ -147,9 +151,11 @@ function Hardware() { if (over && active.id !== over.id) { // Find the indices of the dragged item and the drop target const oldIndex = hardwareData.gpus.findIndex( - (gpu) => gpu.id === active.id + (gpu, index) => index === active.id + ) + const newIndex = hardwareData.gpus.findIndex( + (gpu, index) => index === over.id ) - const newIndex = hardwareData.gpus.findIndex((gpu) => gpu.id === over.id) if (oldIndex !== -1 && newIndex !== -1) { reorderGPUs(oldIndex, newIndex) @@ -160,14 +166,13 @@ function Hardware() { useEffect(() => { if (pollingPaused) return const intervalId = setInterval(() => { - getHardwareInfo().then((data) => { - updateCPUUsage(data.cpu.usage) - updateRAMAvailable(data.ram.available) + getSystemUsage().then((data) => { + updateSystemUsage(data) }) }, 5000) return () => clearInterval(intervalId) - }, [setHardwareData, updateCPUUsage, updateRAMAvailable, pollingPaused]) + }, [setHardwareData, updateSystemUsage, pollingPaused]) const handleClickSystemMonitor = async () => { try { @@ -229,8 +234,8 @@ function Hardware() { - {hardwareData.os?.name} + + {hardwareData.os_type} } /> @@ -238,7 +243,7 @@ function Hardware() { title={t('settings:hardware.version')} actions={ - {hardwareData.os?.version} + {hardwareData.os_name} } /> @@ -250,7 +255,7 @@ function Hardware() { title={t('settings:hardware.model')} actions={ - {hardwareData.cpu?.model} + {hardwareData.cpu?.name} } /> @@ -266,17 +271,17 @@ function Hardware() { title={t('settings:hardware.cores')} actions={ - {hardwareData.cpu?.cores} + {hardwareData.cpu?.core_count} } /> - {hardwareData.cpu?.instructions.join(', ').length > 0 && ( + {hardwareData.cpu?.extensions?.join(', ').length > 0 && ( 6} + column={hardwareData.cpu?.extensions.length > 6} actions={ - {hardwareData.cpu?.instructions?.join(', ')} + {hardwareData.cpu?.extensions?.join(', ')} } /> @@ -285,14 +290,14 @@ function Hardware() { title={t('settings:hardware.usage')} actions={
- {hardwareData.cpu?.usage > 0 && ( + {systemUsage.cpu > 0 && ( <> - {hardwareData.cpu?.usage?.toFixed(2)}% + {systemUsage.cpu?.toFixed(2)}% )} @@ -307,7 +312,7 @@ function Hardware() { title={t('settings:hardware.totalRam')} actions={ - {formatMegaBytes(hardwareData.ram.total)} + {formatMegaBytes(hardwareData.total_memory)} } /> @@ -315,7 +320,9 @@ function Hardware() { title={t('settings:hardware.availableRam')} actions={ - {formatMegaBytes(hardwareData.ram?.available)} + {formatMegaBytes( + hardwareData.total_memory - systemUsage.used_memory + )} } /> @@ -323,23 +330,21 @@ function Hardware() { title={t('settings:hardware.usage')} actions={
- {hardwareData.ram?.total > 0 && ( + {hardwareData.total_memory > 0 && ( <> {( - ((hardwareData.ram?.total - - hardwareData.ram?.available) / - hardwareData.ram?.total) * - 100 + toNumber( + systemUsage.used_memory / systemUsage.total_memory + ) * 100 ).toFixed(2)} % @@ -383,15 +388,11 @@ function Hardware() { onDragEnd={handleDragEnd} > gpu.id)} + items={hardwareData.gpus.map((gpu, index) => index)} strategy={verticalListSortingStrategy} > {hardwareData.gpus.map((gpu, index) => ( - + ))} diff --git a/web-app/src/routes/system-monitor.tsx b/web-app/src/routes/system-monitor.tsx index 1cf236448..45f638bf2 100644 --- a/web-app/src/routes/system-monitor.tsx +++ b/web-app/src/routes/system-monitor.tsx @@ -1,7 +1,7 @@ import { createFileRoute } from '@tanstack/react-router' import { useEffect, useState } from 'react' import { useHardware } from '@/hooks/useHardware' -import { getHardwareInfo } from '@/services/hardware' +import { getHardwareInfo, getSystemUsage } from '@/services/hardware' import { Progress } from '@/components/ui/progress' import type { HardwareData } from '@/hooks/useHardware' import { route } from '@/constants/routes' @@ -10,6 +10,7 @@ import { IconDeviceDesktopAnalytics } from '@tabler/icons-react' import { getActiveModels, stopModel } from '@/services/models' import { Button } from '@/components/ui/button' import { useTranslation } from '@/i18n/react-i18next-compat' +import { toNumber } from '@/utils/number' // eslint-disable-next-line @typescript-eslint/no-explicit-any export const Route = createFileRoute(route.systemMonitor as any)({ @@ -18,7 +19,7 @@ export const Route = createFileRoute(route.systemMonitor as any)({ function SystemMonitor() { const { t } = useTranslation() - const { hardwareData, setHardwareData, updateCPUUsage, updateRAMAvailable } = + const { hardwareData, systemUsage, setHardwareData, updateSystemUsage } = useHardware() const [activeModels, setActiveModels] = useState([]) @@ -31,16 +32,15 @@ function SystemMonitor() { // Set up interval for real-time updates const intervalId = setInterval(() => { - getHardwareInfo().then((data) => { - setHardwareData(data as unknown as HardwareData) - updateCPUUsage(data.cpu?.usage) - updateRAMAvailable(data.ram?.available) + getSystemUsage().then((data) => { + // setHardwareData(data as unknown as HardwareData) + updateSystemUsage(data) }) getActiveModels().then(setActiveModels) }, 5000) return () => clearInterval(intervalId) - }, [setHardwareData, setActiveModels, updateCPUUsage, updateRAMAvailable]) + }, [setHardwareData, setActiveModels, updateSystemUsage]) const stopRunningModel = (modelId: string) => { stopModel(modelId) @@ -56,9 +56,10 @@ function SystemMonitor() { // Calculate RAM usage percentage const ramUsagePercentage = - ((hardwareData.ram.total - hardwareData.ram.available) / - hardwareData.ram.total) * - 100 + toNumber( + (hardwareData.total_memory - systemUsage.used_memory) / + hardwareData.total_memory + ) * 100 return (
@@ -80,16 +81,14 @@ function SystemMonitor() { {t('system-monitor:model')} - - {hardwareData.cpu.model} - + {hardwareData.cpu.name}
{t('system-monitor:cores')} - {hardwareData.cpu.cores} + {hardwareData.cpu.core_count}
@@ -104,10 +103,10 @@ function SystemMonitor() { {t('system-monitor:currentUsage')} - {hardwareData.cpu.usage.toFixed(2)}% + {systemUsage.cpu.toFixed(2)}%
- +
@@ -123,7 +122,7 @@ function SystemMonitor() { {t('system-monitor:totalRam')} - {formatMegaBytes(hardwareData.ram.total)} + {formatMegaBytes(hardwareData.total_memory)}
@@ -131,7 +130,9 @@ function SystemMonitor() { {t('system-monitor:availableRam')} - {formatMegaBytes(hardwareData.ram.available)} + {formatMegaBytes( + hardwareData.total_memory - systemUsage.used_memory + )}
@@ -140,7 +141,7 @@ function SystemMonitor() { {formatMegaBytes( - hardwareData.ram.total - hardwareData.ram.available + hardwareData.total_memory - systemUsage.used_memory )}
@@ -222,10 +223,10 @@ function SystemMonitor() { {hardwareData.gpus.length > 0 ? (
{hardwareData.gpus - .filter((gpu) => gpu.activated) + // .filter((gpu) => gpu.activated) .map((gpu, index) => (
@@ -242,8 +243,11 @@ function SystemMonitor() { {t('system-monitor:vramUsage')} - {formatMegaBytes(gpu.total_vram - gpu.free_vram)} /{' '} - {formatMegaBytes(gpu.total_vram)} + {formatMegaBytes( + gpu.total_memory - + systemUsage.gpus[index]?.used_memory + )}{' '} + / {formatMegaBytes(gpu.total_memory)}
@@ -251,7 +255,7 @@ function SystemMonitor() { {t('system-monitor:driverVersion')} - {gpu.additional_information?.driver_version || '-'} + {gpu.driver_version || '-'}
@@ -259,13 +263,16 @@ function SystemMonitor() { {t('system-monitor:computeCapability')} - {gpu.additional_information?.compute_cap || '-'} + {gpu.nvidia_info?.compute_capability || + gpu.vulkan_info.api_version}
)} - {hardwareData.gpus.length > 0 && - !hardwareData.gpus.some((gpu) => gpu.activated) && ( -
- {t('system-monitor:noActiveGpus')} -
- )}
) diff --git a/web-app/src/services/hardware.ts b/web-app/src/services/hardware.ts index ab06b503a..c0615e858 100644 --- a/web-app/src/services/hardware.ts +++ b/web-app/src/services/hardware.ts @@ -1,24 +1,20 @@ -import { ExtensionManager } from '@/lib/extension' -import { ExtensionTypeEnum, HardwareManagementExtension } from '@janhq/core' +import { HardwareData, SystemUsage } from '@/hooks/useHardware' +import { invoke } from '@tauri-apps/api/core' /** * Get hardware information from the HardwareManagementExtension. * @returns {Promise} A promise that resolves to the hardware information. */ export const getHardwareInfo = async () => { - const extension = - ExtensionManager.getInstance().get( - ExtensionTypeEnum.Hardware - ) + return invoke('get_system_info') as Promise +} - if (!extension) throw new Error('Hardware extension not found') - - try { - return await extension?.getHardware() - } catch (error) { - console.error('Failed to download model:', error) - throw error - } +/** + * Get hardware information from the HardwareManagementExtension. + * @returns {Promise} A promise that resolves to the hardware information. + */ +export const getSystemUsage = async () => { + return invoke('get_system_usage') as Promise } /** @@ -26,20 +22,6 @@ export const getHardwareInfo = async () => { * @returns A Promise that resolves set gpus activate. */ export const setActiveGpus = async (data: { gpus: number[] }) => { - const extension = - ExtensionManager.getInstance().get( - ExtensionTypeEnum.Hardware - ) - - if (!extension) { - throw new Error('Extension is not available') - } - - try { - const response = await extension.setActiveGpu(data) - return response - } catch (error) { - console.error('Failed to install engine variant:', error) - throw error - } + // TODO: llama.cpp extension should handle this + console.log(data) }