fix: gpu hardware state (#4650)

* fix: gpu hardware state

* chore: cleanup state

* chore: clear state
This commit is contained in:
Faisal Amir 2025-02-14 11:21:18 +07:00 committed by GitHub
parent 7a6890bd7f
commit dde260e723
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 186 additions and 142 deletions

View File

@ -509,61 +509,61 @@ __metadata:
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=2e50a2&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=429572&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/92904b534f4f82c31effd9eade0f8685b87165e28f0c3ba3a0c18a6a0a21bf21ee769a98c562c4ba7488ceb1238e2dacce2bb2f729c1bdd84ad44054fb985da0
checksum: 10c0/18b8ca8795ccf2c89e2225668ce2824e4ce7b32872764a739155d2b68a773676188186d721e70dbcf2605bb28697b97d6c01348d34bc440e829087ebd6a3158c
languageName: node
linkType: hard
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=2e50a2&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=429572&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/92904b534f4f82c31effd9eade0f8685b87165e28f0c3ba3a0c18a6a0a21bf21ee769a98c562c4ba7488ceb1238e2dacce2bb2f729c1bdd84ad44054fb985da0
checksum: 10c0/18b8ca8795ccf2c89e2225668ce2824e4ce7b32872764a739155d2b68a773676188186d721e70dbcf2605bb28697b97d6c01348d34bc440e829087ebd6a3158c
languageName: node
linkType: hard
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=2e50a2&locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=429572&locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/92904b534f4f82c31effd9eade0f8685b87165e28f0c3ba3a0c18a6a0a21bf21ee769a98c562c4ba7488ceb1238e2dacce2bb2f729c1bdd84ad44054fb985da0
checksum: 10c0/18b8ca8795ccf2c89e2225668ce2824e4ce7b32872764a739155d2b68a773676188186d721e70dbcf2605bb28697b97d6c01348d34bc440e829087ebd6a3158c
languageName: node
linkType: hard
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fhardware-management-extension%40workspace%3Ahardware-management-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=2e50a2&locator=%40janhq%2Fhardware-management-extension%40workspace%3Ahardware-management-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=429572&locator=%40janhq%2Fhardware-management-extension%40workspace%3Ahardware-management-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/92904b534f4f82c31effd9eade0f8685b87165e28f0c3ba3a0c18a6a0a21bf21ee769a98c562c4ba7488ceb1238e2dacce2bb2f729c1bdd84ad44054fb985da0
checksum: 10c0/18b8ca8795ccf2c89e2225668ce2824e4ce7b32872764a739155d2b68a773676188186d721e70dbcf2605bb28697b97d6c01348d34bc440e829087ebd6a3158c
languageName: node
linkType: hard
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=2e50a2&locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=429572&locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/92904b534f4f82c31effd9eade0f8685b87165e28f0c3ba3a0c18a6a0a21bf21ee769a98c562c4ba7488ceb1238e2dacce2bb2f729c1bdd84ad44054fb985da0
checksum: 10c0/18b8ca8795ccf2c89e2225668ce2824e4ce7b32872764a739155d2b68a773676188186d721e70dbcf2605bb28697b97d6c01348d34bc440e829087ebd6a3158c
languageName: node
linkType: hard
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=2e50a2&locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=429572&locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/92904b534f4f82c31effd9eade0f8685b87165e28f0c3ba3a0c18a6a0a21bf21ee769a98c562c4ba7488ceb1238e2dacce2bb2f729c1bdd84ad44054fb985da0
checksum: 10c0/18b8ca8795ccf2c89e2225668ce2824e4ce7b32872764a739155d2b68a773676188186d721e70dbcf2605bb28697b97d6c01348d34bc440e829087ebd6a3158c
languageName: node
linkType: hard

View File

@ -4,7 +4,7 @@ import * as React from 'react'
import { useState } from 'react'
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'
import { Gpu } from '@janhq/core'
import { Progress, ScrollArea, Switch } from '@janhq/joi'
import { useAtom, useAtomValue } from 'jotai'
import { atomWithStorage } from 'jotai/utils'
@ -20,19 +20,24 @@ import {
import { toGibibytes } from '@/utils/converter'
import { utilizedMemory } from '@/utils/memory'
import {
cpuUsageAtom,
ramUtilitizedAtom,
totalRamAtom,
usedRamAtom,
gpusAtom,
} from '@/helpers/atoms/SystemBar.atom'
const gpusAtom = atomWithStorage<Gpu[]>('gpus', [], undefined, {
const orderGpusAtom = atomWithStorage<any>('orderGpus', [], undefined, {
getOnInit: true,
})
const Hardware = () => {
const { hardware } = useGetHardwareInfo()
const { hardware, mutate } = useGetHardwareInfo()
const [isActivatingGpu, setIsActivatingGpu] = useState<Set<string>>(new Set())
const [openPanels, setOpenPanels] = useState<Record<number, boolean>>({})
const cpuUsage = useAtomValue(cpuUsageAtom)
@ -40,7 +45,9 @@ const Hardware = () => {
const usedRam = useAtomValue(usedRamAtom)
const ramUtilitized = useAtomValue(ramUtilitizedAtom)
const [gpus, setGpus] = useAtom<Gpu[]>(gpusAtom)
const [gpus, setGpus] = useAtom(gpusAtom)
const [orderGpus, setOrderGpus] = useAtom(orderGpusAtom)
const togglePanel = (index: number) => {
setOpenPanels((prev) => ({
@ -51,16 +58,18 @@ const Hardware = () => {
// Handle switch toggle for GPU activation
const handleSwitchChange = async (id: string, isActive: boolean) => {
setIsActivatingGpu((prev) => new Set(prev).add(id))
const updatedGpus = gpus.map((gpu) =>
gpu.id === id ? { ...gpu, activated: isActive } : gpu
)
setGpus(updatedGpus)
// Call the API to update the active GPUs
try {
const activeGpuIds = updatedGpus
.filter((gpu) => gpu.activated)
.map((gpu) => Number(gpu.id))
.filter((gpu: any) => gpu.activated)
.map((gpu: any) => Number(gpu.id))
await setActiveGpus({ gpus: activeGpuIds })
mutate()
} catch (error) {
console.error('Failed to update active GPUs:', error)
}
@ -71,23 +80,36 @@ const Hardware = () => {
const reorderedGpus = Array.from(gpus)
const [movedGpu] = reorderedGpus.splice(result.source.index, 1)
reorderedGpus.splice(result.destination.index, 0, movedGpu)
setGpus(reorderedGpus) // Update the atom, which persists to localStorage
setGpus(reorderedGpus)
setOrderGpus(reorderedGpus.map((gpu) => gpu.id))
}
React.useEffect(() => {
if (hardware?.gpus) {
setGpus((prevGpus) => {
setGpus((prevGpus: any) => {
// Create a map of existing GPUs by UUID for quick lookup
const gpuMap = new Map(prevGpus.map((gpu) => [gpu.uuid, gpu]))
const gpuMap = new Map(prevGpus.map((gpu: any) => [gpu.uuid, gpu]))
// Update existing GPUs or add new ones
const updatedGpus = hardware.gpus.map((newGpu) => {
const existingGpu = gpuMap.get(newGpu.uuid)
const existingGpu: any = gpuMap.get(newGpu.uuid)
if (existingGpu) {
// Update the GPU properties while keeping the original order
if (existingGpu.activated !== newGpu.activated) {
setIsActivatingGpu((prev) => {
const updated = new Set(prev)
updated.delete(existingGpu.id)
updated.clear()
return updated
})
}
return {
...existingGpu,
activated: newGpu.activated,
free_vram: newGpu.free_vram,
total_vram: newGpu.total_vram,
}
@ -100,7 +122,8 @@ const Hardware = () => {
// Append GPUs from the previous state that are not in the hardware.gpus
// This preserves user-reordered GPUs that aren't present in the new data
const remainingGpus = prevGpus.filter(
(prevGpu) => !hardware.gpus?.some((gpu) => gpu.uuid === prevGpu.uuid)
(prevGpu: any) =>
!hardware.gpus?.some((gpu) => gpu.uuid === prevGpu.uuid)
)
return [...updatedGpus, ...remainingGpus]
@ -202,8 +225,25 @@ const Hardware = () => {
ref={provided.innerRef}
className="mt-4"
>
{gpus.map((item, i) => (
<Draggable key={i} draggableId={String(i)} index={i}>
{gpus
.sort((a, b) => {
const orderA = orderGpus.indexOf(a.id)
const orderB = orderGpus.indexOf(b.id)
return orderA - orderB
})
.map((item: any, i) => {
const gpuUtilization = utilizedMemory(
item.free_vram,
item.total_vram
)
const isLoading = isActivatingGpu.has(item.id)
return (
<Draggable
key={i}
draggableId={String(i)}
index={i}
>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
@ -239,23 +279,12 @@ const Hardware = () => {
{item.activated && (
<div className="flex w-40 items-center gap-3">
<Progress
value={Math.round(
((Number(item.total_vram) -
Number(item.free_vram)) /
Number(item.total_vram)) *
100
)}
value={gpuUtilization}
size="small"
className="w-full"
/>
<span className="font-medium">
{Math.round(
((Number(item.total_vram) -
Number(item.free_vram)) /
Number(item.total_vram)) *
100
).toFixed()}
%
{gpuUtilization}%
</span>
</div>
)}
@ -281,6 +310,13 @@ const Hardware = () => {
<Switch
checked={item.activated}
className={twMerge(
isLoading && 'pointer-events-none'
)}
disabled={
Boolean(isActivatingGpu.size) &&
!isLoading
}
onChange={(e) =>
handleSwitchChange(
item.id,
@ -289,6 +325,10 @@ const Hardware = () => {
}
/>
{isLoading && (
<div className="ml-2 h-4 w-4 animate-spin rounded-full border-t-2 border-solid border-blue-500" />
)}
<ChevronDownIcon
size={14}
className={twMerge(
@ -320,7 +360,10 @@ const Hardware = () => {
Compute Capability
</div>
<span>
{item.additional_information?.compute_cap}
{
item.additional_information
?.compute_cap
}
</span>
</div>
</div>
@ -328,7 +371,8 @@ const Hardware = () => {
</div>
)}
</Draggable>
))}
)
})}
{provided.placeholder}
</div>
)}