feat: use hardware information api
This commit is contained in:
parent
d264220245
commit
9b730058b4
@ -24,7 +24,14 @@ impl CpuStaticInfo {
|
|||||||
let name = system
|
let name = system
|
||||||
.cpus()
|
.cpus()
|
||||||
.first()
|
.first()
|
||||||
.map(|cpu| cpu.brand())
|
.map(|cpu| {
|
||||||
|
let brand = cpu.brand();
|
||||||
|
if brand.is_empty() {
|
||||||
|
cpu.name()
|
||||||
|
} else {
|
||||||
|
brand
|
||||||
|
}
|
||||||
|
})
|
||||||
.unwrap_or("unknown")
|
.unwrap_or("unknown")
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,9 @@ import { setActiveGpus } from '@/services/hardware'
|
|||||||
// Hardware data types
|
// Hardware data types
|
||||||
export interface CPU {
|
export interface CPU {
|
||||||
arch: string
|
arch: string
|
||||||
cores: number
|
core_count: number
|
||||||
instructions: string[]
|
extensions: string[]
|
||||||
model: string
|
name: string
|
||||||
usage: number
|
usage: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,14 +18,21 @@ export interface GPUAdditionalInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface GPU {
|
export interface GPU {
|
||||||
activated: boolean
|
|
||||||
additional_information: GPUAdditionalInfo
|
|
||||||
free_vram: number
|
|
||||||
id: string
|
|
||||||
name: string
|
name: string
|
||||||
total_vram: number
|
total_memory: number
|
||||||
|
vendor: string
|
||||||
uuid: 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 {
|
export interface OS {
|
||||||
@ -41,33 +48,48 @@ export interface RAM {
|
|||||||
export interface HardwareData {
|
export interface HardwareData {
|
||||||
cpu: CPU
|
cpu: CPU
|
||||||
gpus: GPU[]
|
gpus: GPU[]
|
||||||
os: OS
|
os_type: string
|
||||||
ram: RAM
|
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
|
// Default values
|
||||||
const defaultHardwareData: HardwareData = {
|
const defaultHardwareData: HardwareData = {
|
||||||
cpu: {
|
cpu: {
|
||||||
arch: '',
|
arch: '',
|
||||||
cores: 0,
|
core_count: 0,
|
||||||
instructions: [],
|
extensions: [],
|
||||||
model: '',
|
name: '',
|
||||||
usage: 0,
|
usage: 0,
|
||||||
},
|
},
|
||||||
gpus: [],
|
gpus: [],
|
||||||
os: {
|
os_type: '',
|
||||||
name: '',
|
os_name: '',
|
||||||
version: '',
|
total_memory: 0,
|
||||||
},
|
}
|
||||||
ram: {
|
|
||||||
available: 0,
|
const defaultSystemUsage: SystemUsage = {
|
||||||
total: 0,
|
cpu: 0,
|
||||||
},
|
used_memory: 0,
|
||||||
|
total_memory: 0,
|
||||||
|
gpus: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HardwareStore {
|
interface HardwareStore {
|
||||||
// Hardware data
|
// Hardware data
|
||||||
hardwareData: HardwareData
|
hardwareData: HardwareData
|
||||||
|
systemUsage: SystemUsage
|
||||||
|
|
||||||
// Update functions
|
// Update functions
|
||||||
setCPU: (cpu: CPU) => void
|
setCPU: (cpu: CPU) => void
|
||||||
@ -81,11 +103,8 @@ interface HardwareStore {
|
|||||||
// Update individual GPU
|
// Update individual GPU
|
||||||
updateGPU: (index: number, gpu: GPU) => void
|
updateGPU: (index: number, gpu: GPU) => void
|
||||||
|
|
||||||
// Update CPU usage
|
|
||||||
updateCPUUsage: (usage: number) => void
|
|
||||||
|
|
||||||
// Update RAM available
|
// Update RAM available
|
||||||
updateRAMAvailable: (available: number) => void
|
updateSystemUsage: (usage: SystemUsage) => void
|
||||||
|
|
||||||
// Toggle GPU activation (async, with loading)
|
// Toggle GPU activation (async, with loading)
|
||||||
toggleGPUActivation: (index: number) => Promise<void>
|
toggleGPUActivation: (index: number) => Promise<void>
|
||||||
@ -107,11 +126,15 @@ export const useHardware = create<HardwareStore>()(
|
|||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
hardwareData: defaultHardwareData,
|
hardwareData: defaultHardwareData,
|
||||||
|
systemUsage: defaultSystemUsage,
|
||||||
gpuLoading: {},
|
gpuLoading: {},
|
||||||
pollingPaused: false,
|
pollingPaused: false,
|
||||||
setGpuLoading: (index, loading) =>
|
setGpuLoading: (index, loading) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
gpuLoading: { ...state.gpuLoading, [state.hardwareData.gpus[index].uuid]: loading },
|
gpuLoading: {
|
||||||
|
...state.gpuLoading,
|
||||||
|
[state.hardwareData.gpus[index].uuid]: loading,
|
||||||
|
},
|
||||||
})),
|
})),
|
||||||
pausePolling: () => set({ pollingPaused: true }),
|
pausePolling: () => set({ pollingPaused: true }),
|
||||||
resumePolling: () => set({ pollingPaused: false }),
|
resumePolling: () => set({ pollingPaused: false }),
|
||||||
@ -167,56 +190,41 @@ export const useHardware = create<HardwareStore>()(
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updateCPUUsage: (usage) =>
|
updateSystemUsage: (systemUsage) =>
|
||||||
set((state) => ({
|
set(() => ({
|
||||||
hardwareData: {
|
systemUsage,
|
||||||
...state.hardwareData,
|
|
||||||
cpu: {
|
|
||||||
...state.hardwareData.cpu,
|
|
||||||
usage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
|
|
||||||
updateRAMAvailable: (available) =>
|
|
||||||
set((state) => ({
|
|
||||||
hardwareData: {
|
|
||||||
...state.hardwareData,
|
|
||||||
ram: {
|
|
||||||
...state.hardwareData.ram,
|
|
||||||
available,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})),
|
})),
|
||||||
|
|
||||||
toggleGPUActivation: async (index) => {
|
toggleGPUActivation: async (index) => {
|
||||||
const { pausePolling, setGpuLoading, resumePolling } = get();
|
const { pausePolling, setGpuLoading, resumePolling } = get()
|
||||||
pausePolling();
|
pausePolling()
|
||||||
setGpuLoading(index, true);
|
setGpuLoading(index, true)
|
||||||
try {
|
// try {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 200)); // Simulate async, replace with real API if needed
|
// await new Promise((resolve) => setTimeout(resolve, 200)) // Simulate async, replace with real API if needed
|
||||||
set((state) => {
|
// set((state) => {
|
||||||
const newGPUs = [...state.hardwareData.gpus];
|
// const newGPUs = [...state.hardwareData.gpus]
|
||||||
if (index >= 0 && index < newGPUs.length) {
|
// if (index >= 0 && index < newGPUs.length) {
|
||||||
newGPUs[index] = {
|
// newGPUs[index] = {
|
||||||
...newGPUs[index],
|
// ...newGPUs[index],
|
||||||
activated: !newGPUs[index].activated,
|
// activated: !newGPUs[index].activated,
|
||||||
};
|
// }
|
||||||
}
|
// }
|
||||||
setActiveGpus({
|
// setActiveGpus({
|
||||||
gpus: newGPUs.filter((e) => e.activated).map((e) => parseInt(e.id)),
|
// gpus: newGPUs
|
||||||
});
|
// .filter((e) => e.activated)
|
||||||
return {
|
// .map((e) => parseInt(e.id)),
|
||||||
hardwareData: {
|
// })
|
||||||
...state.hardwareData,
|
// return {
|
||||||
gpus: newGPUs,
|
// hardwareData: {
|
||||||
},
|
// ...state.hardwareData,
|
||||||
};
|
// gpus: newGPUs,
|
||||||
});
|
// },
|
||||||
} finally {
|
// }
|
||||||
setGpuLoading(index, false);
|
// })
|
||||||
setTimeout(resumePolling, 1000); // Resume polling after 1s
|
// } finally {
|
||||||
}
|
// setGpuLoading(index, false)
|
||||||
|
// setTimeout(resumePolling, 1000) // Resume polling after 1s
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
|
|
||||||
reorderGPUs: (oldIndex, newIndex) =>
|
reorderGPUs: (oldIndex, newIndex) =>
|
||||||
|
|||||||
@ -29,10 +29,11 @@ import {
|
|||||||
IconGripVertical,
|
IconGripVertical,
|
||||||
IconDeviceDesktopAnalytics,
|
IconDeviceDesktopAnalytics,
|
||||||
} from '@tabler/icons-react'
|
} from '@tabler/icons-react'
|
||||||
import { getHardwareInfo } from '@/services/hardware'
|
import { getHardwareInfo, getSystemUsage } from '@/services/hardware'
|
||||||
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
|
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
|
||||||
import { formatMegaBytes } from '@/lib/utils'
|
import { formatMegaBytes } from '@/lib/utils'
|
||||||
import { windowKey } from '@/constants/windows'
|
import { windowKey } from '@/constants/windows'
|
||||||
|
import { toNumber } from '@/utils/number'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const Route = createFileRoute(route.settings.hardware as any)({
|
export const Route = createFileRoute(route.settings.hardware as any)({
|
||||||
@ -47,10 +48,11 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) {
|
|||||||
transform,
|
transform,
|
||||||
transition,
|
transition,
|
||||||
isDragging,
|
isDragging,
|
||||||
} = useSortable({ id: gpu.id || index })
|
} = useSortable({ id: index })
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { toggleGPUActivation, gpuLoading } = useHardware()
|
const { systemUsage, toggleGPUActivation, gpuLoading } = useHardware()
|
||||||
|
const usage = systemUsage.gpus[index]
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
transform: CSS.Transform.toString(transform),
|
transform: CSS.Transform.toString(transform),
|
||||||
@ -78,7 +80,7 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) {
|
|||||||
actions={
|
actions={
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Switch
|
<Switch
|
||||||
checked={gpu.activated}
|
checked={true}
|
||||||
disabled={!!gpuLoading[index]}
|
disabled={!!gpuLoading[index]}
|
||||||
onCheckedChange={() => toggleGPUActivation(index)}
|
onCheckedChange={() => toggleGPUActivation(index)}
|
||||||
/>
|
/>
|
||||||
@ -90,8 +92,9 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) {
|
|||||||
title={t('settings:hardware.vram')}
|
title={t('settings:hardware.vram')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{formatMegaBytes(gpu.free_vram)} {t('settings:hardware.freeOf')}{' '}
|
{formatMegaBytes(usage?.used_memory)}{' '}
|
||||||
{formatMegaBytes(gpu.total_vram)}
|
{t('settings:hardware.freeOf')}{' '}
|
||||||
|
{formatMegaBytes(gpu.total_memory)}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -99,7 +102,7 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) {
|
|||||||
title={t('settings:hardware.driverVersion')}
|
title={t('settings:hardware.driverVersion')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{gpu.additional_information?.driver_version || '-'}
|
{gpu.driver_version?.slice(0, 50) || '-'}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -107,7 +110,8 @@ function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) {
|
|||||||
title={t('settings:hardware.computeCapability')}
|
title={t('settings:hardware.computeCapability')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{gpu.additional_information?.compute_cap || '-'}
|
{gpu.nvidia_info?.compute_capability ??
|
||||||
|
gpu.vulkan_info?.api_version}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -120,9 +124,9 @@ function Hardware() {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
hardwareData,
|
hardwareData,
|
||||||
|
systemUsage,
|
||||||
setHardwareData,
|
setHardwareData,
|
||||||
updateCPUUsage,
|
updateSystemUsage,
|
||||||
updateRAMAvailable,
|
|
||||||
reorderGPUs,
|
reorderGPUs,
|
||||||
pollingPaused,
|
pollingPaused,
|
||||||
} = useHardware()
|
} = useHardware()
|
||||||
@ -147,9 +151,11 @@ function Hardware() {
|
|||||||
if (over && active.id !== over.id) {
|
if (over && active.id !== over.id) {
|
||||||
// Find the indices of the dragged item and the drop target
|
// Find the indices of the dragged item and the drop target
|
||||||
const oldIndex = hardwareData.gpus.findIndex(
|
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) {
|
if (oldIndex !== -1 && newIndex !== -1) {
|
||||||
reorderGPUs(oldIndex, newIndex)
|
reorderGPUs(oldIndex, newIndex)
|
||||||
@ -160,14 +166,13 @@ function Hardware() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pollingPaused) return
|
if (pollingPaused) return
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
getHardwareInfo().then((data) => {
|
getSystemUsage().then((data) => {
|
||||||
updateCPUUsage(data.cpu.usage)
|
updateSystemUsage(data)
|
||||||
updateRAMAvailable(data.ram.available)
|
|
||||||
})
|
})
|
||||||
}, 5000)
|
}, 5000)
|
||||||
|
|
||||||
return () => clearInterval(intervalId)
|
return () => clearInterval(intervalId)
|
||||||
}, [setHardwareData, updateCPUUsage, updateRAMAvailable, pollingPaused])
|
}, [setHardwareData, updateSystemUsage, pollingPaused])
|
||||||
|
|
||||||
const handleClickSystemMonitor = async () => {
|
const handleClickSystemMonitor = async () => {
|
||||||
try {
|
try {
|
||||||
@ -229,8 +234,8 @@ function Hardware() {
|
|||||||
<CardItem
|
<CardItem
|
||||||
title={t('settings:hardware.name')}
|
title={t('settings:hardware.name')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80 capitalize">
|
||||||
{hardwareData.os?.name}
|
{hardwareData.os_type}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -238,7 +243,7 @@ function Hardware() {
|
|||||||
title={t('settings:hardware.version')}
|
title={t('settings:hardware.version')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{hardwareData.os?.version}
|
{hardwareData.os_name}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -250,7 +255,7 @@ function Hardware() {
|
|||||||
title={t('settings:hardware.model')}
|
title={t('settings:hardware.model')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{hardwareData.cpu?.model}
|
{hardwareData.cpu?.name}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -266,17 +271,17 @@ function Hardware() {
|
|||||||
title={t('settings:hardware.cores')}
|
title={t('settings:hardware.cores')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{hardwareData.cpu?.cores}
|
{hardwareData.cpu?.core_count}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{hardwareData.cpu?.instructions.join(', ').length > 0 && (
|
{hardwareData.cpu?.extensions?.join(', ').length > 0 && (
|
||||||
<CardItem
|
<CardItem
|
||||||
title={t('settings:hardware.instructions')}
|
title={t('settings:hardware.instructions')}
|
||||||
column={hardwareData.cpu?.instructions.length > 6}
|
column={hardwareData.cpu?.extensions.length > 6}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80 break-words">
|
<span className="text-main-view-fg/80 break-words">
|
||||||
{hardwareData.cpu?.instructions?.join(', ')}
|
{hardwareData.cpu?.extensions?.join(', ')}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -285,14 +290,14 @@ function Hardware() {
|
|||||||
title={t('settings:hardware.usage')}
|
title={t('settings:hardware.usage')}
|
||||||
actions={
|
actions={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{hardwareData.cpu?.usage > 0 && (
|
{systemUsage.cpu > 0 && (
|
||||||
<>
|
<>
|
||||||
<Progress
|
<Progress
|
||||||
value={hardwareData.cpu?.usage}
|
value={systemUsage.cpu}
|
||||||
className="h-2 w-10"
|
className="h-2 w-10"
|
||||||
/>
|
/>
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{hardwareData.cpu?.usage?.toFixed(2)}%
|
{systemUsage.cpu?.toFixed(2)}%
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -307,7 +312,7 @@ function Hardware() {
|
|||||||
title={t('settings:hardware.totalRam')}
|
title={t('settings:hardware.totalRam')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{formatMegaBytes(hardwareData.ram.total)}
|
{formatMegaBytes(hardwareData.total_memory)}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -315,7 +320,9 @@ function Hardware() {
|
|||||||
title={t('settings:hardware.availableRam')}
|
title={t('settings:hardware.availableRam')}
|
||||||
actions={
|
actions={
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{formatMegaBytes(hardwareData.ram?.available)}
|
{formatMegaBytes(
|
||||||
|
hardwareData.total_memory - systemUsage.used_memory
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -323,23 +330,21 @@ function Hardware() {
|
|||||||
title={t('settings:hardware.usage')}
|
title={t('settings:hardware.usage')}
|
||||||
actions={
|
actions={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{hardwareData.ram?.total > 0 && (
|
{hardwareData.total_memory > 0 && (
|
||||||
<>
|
<>
|
||||||
<Progress
|
<Progress
|
||||||
value={
|
value={
|
||||||
((hardwareData.ram?.total -
|
toNumber(
|
||||||
hardwareData.ram?.available) /
|
systemUsage.used_memory / systemUsage.total_memory
|
||||||
hardwareData.ram?.total) *
|
) * 100
|
||||||
100
|
|
||||||
}
|
}
|
||||||
className="h-2 w-10"
|
className="h-2 w-10"
|
||||||
/>
|
/>
|
||||||
<span className="text-main-view-fg/80">
|
<span className="text-main-view-fg/80">
|
||||||
{(
|
{(
|
||||||
((hardwareData.ram?.total -
|
toNumber(
|
||||||
hardwareData.ram?.available) /
|
systemUsage.used_memory / systemUsage.total_memory
|
||||||
hardwareData.ram?.total) *
|
) * 100
|
||||||
100
|
|
||||||
).toFixed(2)}
|
).toFixed(2)}
|
||||||
%
|
%
|
||||||
</span>
|
</span>
|
||||||
@ -383,15 +388,11 @@ function Hardware() {
|
|||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
>
|
>
|
||||||
<SortableContext
|
<SortableContext
|
||||||
items={hardwareData.gpus.map((gpu) => gpu.id)}
|
items={hardwareData.gpus.map((gpu, index) => index)}
|
||||||
strategy={verticalListSortingStrategy}
|
strategy={verticalListSortingStrategy}
|
||||||
>
|
>
|
||||||
{hardwareData.gpus.map((gpu, index) => (
|
{hardwareData.gpus.map((gpu, index) => (
|
||||||
<SortableGPUItem
|
<SortableGPUItem key={index} gpu={gpu} index={index} />
|
||||||
key={gpu.id || index}
|
|
||||||
gpu={gpu}
|
|
||||||
index={index}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useHardware } from '@/hooks/useHardware'
|
import { useHardware } from '@/hooks/useHardware'
|
||||||
import { getHardwareInfo } from '@/services/hardware'
|
import { getHardwareInfo, getSystemUsage } from '@/services/hardware'
|
||||||
import { Progress } from '@/components/ui/progress'
|
import { Progress } from '@/components/ui/progress'
|
||||||
import type { HardwareData } from '@/hooks/useHardware'
|
import type { HardwareData } from '@/hooks/useHardware'
|
||||||
import { route } from '@/constants/routes'
|
import { route } from '@/constants/routes'
|
||||||
@ -10,6 +10,7 @@ import { IconDeviceDesktopAnalytics } from '@tabler/icons-react'
|
|||||||
import { getActiveModels, stopModel } from '@/services/models'
|
import { getActiveModels, stopModel } from '@/services/models'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { useTranslation } from '@/i18n/react-i18next-compat'
|
import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||||
|
import { toNumber } from '@/utils/number'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const Route = createFileRoute(route.systemMonitor as any)({
|
export const Route = createFileRoute(route.systemMonitor as any)({
|
||||||
@ -18,7 +19,7 @@ export const Route = createFileRoute(route.systemMonitor as any)({
|
|||||||
|
|
||||||
function SystemMonitor() {
|
function SystemMonitor() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { hardwareData, setHardwareData, updateCPUUsage, updateRAMAvailable } =
|
const { hardwareData, systemUsage, setHardwareData, updateSystemUsage } =
|
||||||
useHardware()
|
useHardware()
|
||||||
const [activeModels, setActiveModels] = useState<string[]>([])
|
const [activeModels, setActiveModels] = useState<string[]>([])
|
||||||
|
|
||||||
@ -31,16 +32,15 @@ function SystemMonitor() {
|
|||||||
|
|
||||||
// Set up interval for real-time updates
|
// Set up interval for real-time updates
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
getHardwareInfo().then((data) => {
|
getSystemUsage().then((data) => {
|
||||||
setHardwareData(data as unknown as HardwareData)
|
// setHardwareData(data as unknown as HardwareData)
|
||||||
updateCPUUsage(data.cpu?.usage)
|
updateSystemUsage(data)
|
||||||
updateRAMAvailable(data.ram?.available)
|
|
||||||
})
|
})
|
||||||
getActiveModels().then(setActiveModels)
|
getActiveModels().then(setActiveModels)
|
||||||
}, 5000)
|
}, 5000)
|
||||||
|
|
||||||
return () => clearInterval(intervalId)
|
return () => clearInterval(intervalId)
|
||||||
}, [setHardwareData, setActiveModels, updateCPUUsage, updateRAMAvailable])
|
}, [setHardwareData, setActiveModels, updateSystemUsage])
|
||||||
|
|
||||||
const stopRunningModel = (modelId: string) => {
|
const stopRunningModel = (modelId: string) => {
|
||||||
stopModel(modelId)
|
stopModel(modelId)
|
||||||
@ -56,9 +56,10 @@ function SystemMonitor() {
|
|||||||
|
|
||||||
// Calculate RAM usage percentage
|
// Calculate RAM usage percentage
|
||||||
const ramUsagePercentage =
|
const ramUsagePercentage =
|
||||||
((hardwareData.ram.total - hardwareData.ram.available) /
|
toNumber(
|
||||||
hardwareData.ram.total) *
|
(hardwareData.total_memory - systemUsage.used_memory) /
|
||||||
100
|
hardwareData.total_memory
|
||||||
|
) * 100
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full bg-main-view overflow-y-auto p-6">
|
<div className="flex flex-col h-full bg-main-view overflow-y-auto p-6">
|
||||||
@ -80,16 +81,14 @@ function SystemMonitor() {
|
|||||||
<span className="text-main-view-fg/70">
|
<span className="text-main-view-fg/70">
|
||||||
{t('system-monitor:model')}
|
{t('system-monitor:model')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg">
|
<span className="text-main-view-fg">{hardwareData.cpu.name}</span>
|
||||||
{hardwareData.cpu.model}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-main-view-fg/70">
|
<span className="text-main-view-fg/70">
|
||||||
{t('system-monitor:cores')}
|
{t('system-monitor:cores')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg">
|
<span className="text-main-view-fg">
|
||||||
{hardwareData.cpu.cores}
|
{hardwareData.cpu.core_count}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
@ -104,10 +103,10 @@ function SystemMonitor() {
|
|||||||
{t('system-monitor:currentUsage')}
|
{t('system-monitor:currentUsage')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg font-bold">
|
<span className="text-main-view-fg font-bold">
|
||||||
{hardwareData.cpu.usage.toFixed(2)}%
|
{systemUsage.cpu.toFixed(2)}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Progress value={hardwareData.cpu.usage} className="h-3 w-full" />
|
<Progress value={systemUsage.cpu} className="h-3 w-full" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -123,7 +122,7 @@ function SystemMonitor() {
|
|||||||
{t('system-monitor:totalRam')}
|
{t('system-monitor:totalRam')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg">
|
<span className="text-main-view-fg">
|
||||||
{formatMegaBytes(hardwareData.ram.total)}
|
{formatMegaBytes(hardwareData.total_memory)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
@ -131,7 +130,9 @@ function SystemMonitor() {
|
|||||||
{t('system-monitor:availableRam')}
|
{t('system-monitor:availableRam')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg">
|
<span className="text-main-view-fg">
|
||||||
{formatMegaBytes(hardwareData.ram.available)}
|
{formatMegaBytes(
|
||||||
|
hardwareData.total_memory - systemUsage.used_memory
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
@ -140,7 +141,7 @@ function SystemMonitor() {
|
|||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg">
|
<span className="text-main-view-fg">
|
||||||
{formatMegaBytes(
|
{formatMegaBytes(
|
||||||
hardwareData.ram.total - hardwareData.ram.available
|
hardwareData.total_memory - systemUsage.used_memory
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -222,10 +223,10 @@ function SystemMonitor() {
|
|||||||
{hardwareData.gpus.length > 0 ? (
|
{hardwareData.gpus.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
{hardwareData.gpus
|
{hardwareData.gpus
|
||||||
.filter((gpu) => gpu.activated)
|
// .filter((gpu) => gpu.activated)
|
||||||
.map((gpu, index) => (
|
.map((gpu, index) => (
|
||||||
<div
|
<div
|
||||||
key={gpu.id || index}
|
key={gpu.uuid || index}
|
||||||
className="bg-main-view-fg/3 rounded-lg p-4"
|
className="bg-main-view-fg/3 rounded-lg p-4"
|
||||||
>
|
>
|
||||||
<div className="flex justify-between items-center mb-2">
|
<div className="flex justify-between items-center mb-2">
|
||||||
@ -242,8 +243,11 @@ function SystemMonitor() {
|
|||||||
{t('system-monitor:vramUsage')}
|
{t('system-monitor:vramUsage')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg">
|
<span className="text-main-view-fg">
|
||||||
{formatMegaBytes(gpu.total_vram - gpu.free_vram)} /{' '}
|
{formatMegaBytes(
|
||||||
{formatMegaBytes(gpu.total_vram)}
|
gpu.total_memory -
|
||||||
|
systemUsage.gpus[index]?.used_memory
|
||||||
|
)}{' '}
|
||||||
|
/ {formatMegaBytes(gpu.total_memory)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
@ -251,7 +255,7 @@ function SystemMonitor() {
|
|||||||
{t('system-monitor:driverVersion')}
|
{t('system-monitor:driverVersion')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg">
|
<span className="text-main-view-fg">
|
||||||
{gpu.additional_information?.driver_version || '-'}
|
{gpu.driver_version || '-'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
@ -259,13 +263,16 @@ function SystemMonitor() {
|
|||||||
{t('system-monitor:computeCapability')}
|
{t('system-monitor:computeCapability')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-main-view-fg">
|
<span className="text-main-view-fg">
|
||||||
{gpu.additional_information?.compute_cap || '-'}
|
{gpu.nvidia_info?.compute_capability ||
|
||||||
|
gpu.vulkan_info.api_version}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<Progress
|
<Progress
|
||||||
value={
|
value={
|
||||||
((gpu.total_vram - gpu.free_vram) / gpu.total_vram) *
|
((gpu.total_memory -
|
||||||
|
systemUsage.gpus[index]?.used_memory) /
|
||||||
|
gpu.total_memory) *
|
||||||
100
|
100
|
||||||
}
|
}
|
||||||
className="h-2 w-full"
|
className="h-2 w-full"
|
||||||
@ -280,12 +287,6 @@ function SystemMonitor() {
|
|||||||
{t('system-monitor:noGpus')}
|
{t('system-monitor:noGpus')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{hardwareData.gpus.length > 0 &&
|
|
||||||
!hardwareData.gpus.some((gpu) => gpu.activated) && (
|
|
||||||
<div className="text-center text-main-view-fg/50 py-4">
|
|
||||||
{t('system-monitor:noActiveGpus')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,24 +1,20 @@
|
|||||||
import { ExtensionManager } from '@/lib/extension'
|
import { HardwareData, SystemUsage } from '@/hooks/useHardware'
|
||||||
import { ExtensionTypeEnum, HardwareManagementExtension } from '@janhq/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get hardware information from the HardwareManagementExtension.
|
* Get hardware information from the HardwareManagementExtension.
|
||||||
* @returns {Promise<HardwareInfo>} A promise that resolves to the hardware information.
|
* @returns {Promise<HardwareInfo>} A promise that resolves to the hardware information.
|
||||||
*/
|
*/
|
||||||
export const getHardwareInfo = async () => {
|
export const getHardwareInfo = async () => {
|
||||||
const extension =
|
return invoke('get_system_info') as Promise<HardwareData>
|
||||||
ExtensionManager.getInstance().get<HardwareManagementExtension>(
|
|
||||||
ExtensionTypeEnum.Hardware
|
|
||||||
)
|
|
||||||
|
|
||||||
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<HardwareInfo>} A promise that resolves to the hardware information.
|
||||||
|
*/
|
||||||
|
export const getSystemUsage = async () => {
|
||||||
|
return invoke('get_system_usage') as Promise<SystemUsage>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,20 +22,6 @@ export const getHardwareInfo = async () => {
|
|||||||
* @returns A Promise that resolves set gpus activate.
|
* @returns A Promise that resolves set gpus activate.
|
||||||
*/
|
*/
|
||||||
export const setActiveGpus = async (data: { gpus: number[] }) => {
|
export const setActiveGpus = async (data: { gpus: number[] }) => {
|
||||||
const extension =
|
// TODO: llama.cpp extension should handle this
|
||||||
ExtensionManager.getInstance().get<HardwareManagementExtension>(
|
console.log(data)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user