🐛fix: immediately show download progress (#5308)
This commit is contained in:
parent
a745d24fbe
commit
f0ec3e03d1
@ -19,7 +19,13 @@ export function DownloadManagement() {
|
|||||||
const { setProviders } = useModelProvider()
|
const { setProviders } = useModelProvider()
|
||||||
const { open: isLeftPanelOpen } = useLeftPanel()
|
const { open: isLeftPanelOpen } = useLeftPanel()
|
||||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
|
||||||
const { downloads, updateProgress, removeDownload } = useDownloadStore()
|
const {
|
||||||
|
downloads,
|
||||||
|
updateProgress,
|
||||||
|
localDownloadingModels,
|
||||||
|
removeDownload,
|
||||||
|
removeLocalDownloadingModel,
|
||||||
|
} = useDownloadStore()
|
||||||
const { updateState } = useAppUpdater()
|
const { updateState } = useAppUpdater()
|
||||||
|
|
||||||
const [appUpdateState, setAppUpdateState] = useState({
|
const [appUpdateState, setAppUpdateState] = useState({
|
||||||
@ -76,23 +82,36 @@ export function DownloadManagement() {
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const downloadProcesses = useMemo(() => {
|
||||||
|
// Get downloads with progress data
|
||||||
|
const downloadsWithProgress = Object.values(downloads).map((download) => ({
|
||||||
|
id: download.name,
|
||||||
|
name: download.name,
|
||||||
|
progress: download.progress,
|
||||||
|
current: download.current,
|
||||||
|
total: download.total,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Add local downloading models that don't have progress data yet
|
||||||
|
const localDownloadsWithoutProgress = Array.from(localDownloadingModels)
|
||||||
|
.filter((modelId) => !downloads[modelId]) // Only include models not in downloads
|
||||||
|
.map((modelId) => ({
|
||||||
|
id: modelId,
|
||||||
|
name: modelId,
|
||||||
|
progress: 0,
|
||||||
|
current: 0,
|
||||||
|
total: 0,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return [...downloadsWithProgress, ...localDownloadsWithoutProgress]
|
||||||
|
}, [downloads, localDownloadingModels])
|
||||||
|
|
||||||
const downloadCount = useMemo(() => {
|
const downloadCount = useMemo(() => {
|
||||||
const modelDownloads = Object.keys(downloads).length
|
const modelDownloads = downloadProcesses.length
|
||||||
const appUpdateDownload = appUpdateState.isDownloading ? 1 : 0
|
const appUpdateDownload = appUpdateState.isDownloading ? 1 : 0
|
||||||
const total = modelDownloads + appUpdateDownload
|
const total = modelDownloads + appUpdateDownload
|
||||||
return total
|
return total
|
||||||
}, [downloads, appUpdateState.isDownloading])
|
}, [downloadProcesses, appUpdateState.isDownloading])
|
||||||
const downloadProcesses = useMemo(
|
|
||||||
() =>
|
|
||||||
Object.values(downloads).map((download) => ({
|
|
||||||
id: download.name,
|
|
||||||
name: download.name,
|
|
||||||
progress: download.progress,
|
|
||||||
current: download.current,
|
|
||||||
total: download.total,
|
|
||||||
})),
|
|
||||||
[downloads]
|
|
||||||
)
|
|
||||||
|
|
||||||
const overallProgress = useMemo(() => {
|
const overallProgress = useMemo(() => {
|
||||||
const modelTotal = downloadProcesses.reduce((acc, download) => {
|
const modelTotal = downloadProcesses.reduce((acc, download) => {
|
||||||
@ -139,29 +158,32 @@ export function DownloadManagement() {
|
|||||||
(state: DownloadState) => {
|
(state: DownloadState) => {
|
||||||
console.debug('onFileDownloadError', state)
|
console.debug('onFileDownloadError', state)
|
||||||
removeDownload(state.modelId)
|
removeDownload(state.modelId)
|
||||||
|
removeLocalDownloadingModel(state.modelId)
|
||||||
},
|
},
|
||||||
[removeDownload]
|
[removeDownload, removeLocalDownloadingModel]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onFileDownloadStopped = useCallback(
|
const onFileDownloadStopped = useCallback(
|
||||||
(state: DownloadState) => {
|
(state: DownloadState) => {
|
||||||
console.debug('onFileDownloadError', state)
|
console.debug('onFileDownloadError', state)
|
||||||
removeDownload(state.modelId)
|
removeDownload(state.modelId)
|
||||||
|
removeLocalDownloadingModel(state.modelId)
|
||||||
},
|
},
|
||||||
[removeDownload]
|
[removeDownload, removeLocalDownloadingModel]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onFileDownloadSuccess = useCallback(
|
const onFileDownloadSuccess = useCallback(
|
||||||
async (state: DownloadState) => {
|
async (state: DownloadState) => {
|
||||||
console.debug('onFileDownloadSuccess', state)
|
console.debug('onFileDownloadSuccess', state)
|
||||||
removeDownload(state.modelId)
|
removeDownload(state.modelId)
|
||||||
|
removeLocalDownloadingModel(state.modelId)
|
||||||
getProviders().then(setProviders)
|
getProviders().then(setProviders)
|
||||||
toast.success('Download Complete', {
|
toast.success('Download Complete', {
|
||||||
id: 'download-complete',
|
id: 'download-complete',
|
||||||
description: `The model ${state.modelId} has been downloaded`,
|
description: `The model ${state.modelId} has been downloaded`,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[removeDownload, setProviders]
|
[removeDownload, removeLocalDownloadingModel, setProviders]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -264,12 +286,16 @@ export function DownloadManagement() {
|
|||||||
/>
|
/>
|
||||||
<p className="text-main-view-fg/60 text-xs">
|
<p className="text-main-view-fg/60 text-xs">
|
||||||
{`${renderGB(appUpdateState.downloadedBytes)} / ${renderGB(appUpdateState.totalBytes)}`}{' '}
|
{`${renderGB(appUpdateState.downloadedBytes)} / ${renderGB(appUpdateState.totalBytes)}`}{' '}
|
||||||
GB ({Math.round(appUpdateState.downloadProgress * 100)}%)
|
GB ({Math.round(appUpdateState.downloadProgress * 100)}
|
||||||
|
%)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{downloadProcesses.map((download) => (
|
{downloadProcesses.map((download) => (
|
||||||
<div className="bg-main-view-fg/4 rounded-md p-2">
|
<div
|
||||||
|
key={download.id}
|
||||||
|
className="bg-main-view-fg/4 rounded-md p-2"
|
||||||
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<p className="truncate text-main-view-fg/80">
|
<p className="truncate text-main-view-fg/80">
|
||||||
{download.name}
|
{download.name}
|
||||||
@ -299,8 +325,9 @@ export function DownloadManagement() {
|
|||||||
className="my-2"
|
className="my-2"
|
||||||
/>
|
/>
|
||||||
<p className="text-main-view-fg/60 text-xs">
|
<p className="text-main-view-fg/60 text-xs">
|
||||||
{`${renderGB(download.current)} / ${renderGB(download.total)}`}{' '}
|
{download.total > 0
|
||||||
GB ({Math.round(download.progress * 100)}%)
|
? `${renderGB(download.current)} / ${renderGB(download.total)} GB (${Math.round(download.progress * 100)}%)`
|
||||||
|
: 'Initializing download...'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export interface DownloadProgressProps {
|
|||||||
// Zustand store for thinking block state
|
// Zustand store for thinking block state
|
||||||
export type DownloadState = {
|
export type DownloadState = {
|
||||||
downloads: { [id: string]: DownloadProgressProps }
|
downloads: { [id: string]: DownloadProgressProps }
|
||||||
|
localDownloadingModels: Set<string>
|
||||||
removeDownload: (id: string) => void
|
removeDownload: (id: string) => void
|
||||||
updateProgress: (
|
updateProgress: (
|
||||||
id: string,
|
id: string,
|
||||||
@ -19,6 +20,8 @@ export type DownloadState = {
|
|||||||
current?: number,
|
current?: number,
|
||||||
total?: number
|
total?: number
|
||||||
) => void
|
) => void
|
||||||
|
addLocalDownloadingModel: (modelId: string) => void
|
||||||
|
removeLocalDownloadingModel: (modelId: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,6 +29,7 @@ export type DownloadState = {
|
|||||||
*/
|
*/
|
||||||
export const useDownloadStore = create<DownloadState>((set) => ({
|
export const useDownloadStore = create<DownloadState>((set) => ({
|
||||||
downloads: {},
|
downloads: {},
|
||||||
|
localDownloadingModels: new Set(),
|
||||||
removeDownload: (id: string) =>
|
removeDownload: (id: string) =>
|
||||||
set((state) => {
|
set((state) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
@ -46,4 +50,18 @@ export const useDownloadStore = create<DownloadState>((set) => ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
|
||||||
|
addLocalDownloadingModel: (modelId: string) =>
|
||||||
|
set((state) => ({
|
||||||
|
localDownloadingModels: new Set(state.localDownloadingModels).add(
|
||||||
|
modelId
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
|
||||||
|
removeLocalDownloadingModel: (modelId: string) =>
|
||||||
|
set((state) => {
|
||||||
|
const newSet = new Set(state.localDownloadingModels)
|
||||||
|
newSet.delete(modelId)
|
||||||
|
return { localDownloadingModels: newSet }
|
||||||
|
}),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -182,7 +182,8 @@ function Hub() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { downloads } = useDownloadStore()
|
const { downloads, localDownloadingModels, addLocalDownloadingModel } =
|
||||||
|
useDownloadStore()
|
||||||
|
|
||||||
const downloadProcesses = useMemo(
|
const downloadProcesses = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -225,7 +226,9 @@ function Hub() {
|
|||||||
model.models.find((e) =>
|
model.models.find((e) =>
|
||||||
defaultModelQuantizations.some((m) => e.id.toLowerCase().includes(m))
|
defaultModelQuantizations.some((m) => e.id.toLowerCase().includes(m))
|
||||||
)?.id ?? model.models[0]?.id
|
)?.id ?? model.models[0]?.id
|
||||||
const isDownloading = downloadProcesses.some((e) => e.id === modelId)
|
const isDownloading =
|
||||||
|
localDownloadingModels.has(modelId) ||
|
||||||
|
downloadProcesses.some((e) => e.id === modelId)
|
||||||
const downloadProgress =
|
const downloadProgress =
|
||||||
downloadProcesses.find((e) => e.id === modelId)?.progress || 0
|
downloadProcesses.find((e) => e.id === modelId)?.progress || 0
|
||||||
const isDownloaded = llamaProvider?.models.some(
|
const isDownloaded = llamaProvider?.models.some(
|
||||||
@ -233,6 +236,12 @@ function Hub() {
|
|||||||
)
|
)
|
||||||
const isRecommended = isRecommendedModel(model.metadata?.id)
|
const isRecommended = isRecommendedModel(model.metadata?.id)
|
||||||
|
|
||||||
|
const handleDownload = () => {
|
||||||
|
// Immediately set local downloading state
|
||||||
|
addLocalDownloadingModel(modelId)
|
||||||
|
downloadModel(modelId)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -255,7 +264,7 @@ function Hub() {
|
|||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => downloadModel(modelId)}
|
onClick={handleDownload}
|
||||||
className={cn(isDownloading && 'hidden')}
|
className={cn(isDownloading && 'hidden')}
|
||||||
ref={isRecommended ? downloadButtonRef : undefined}
|
ref={isRecommended ? downloadButtonRef : undefined}
|
||||||
>
|
>
|
||||||
@ -271,6 +280,8 @@ function Hub() {
|
|||||||
handleUseModel,
|
handleUseModel,
|
||||||
isRecommendedModel,
|
isRecommendedModel,
|
||||||
downloadButtonRef,
|
downloadButtonRef,
|
||||||
|
localDownloadingModels,
|
||||||
|
addLocalDownloadingModel,
|
||||||
])
|
])
|
||||||
|
|
||||||
const { step } = useSearch({ from: Route.id })
|
const { step } = useSearch({ from: Route.id })
|
||||||
@ -320,7 +331,8 @@ function Hub() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if any model is currently downloading
|
// Check if any model is currently downloading
|
||||||
const isDownloading = downloadProcesses.length > 0
|
const isDownloading =
|
||||||
|
localDownloadingModels.size > 0 || downloadProcesses.length > 0
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
@ -553,6 +565,9 @@ function Hub() {
|
|||||||
</p>
|
</p>
|
||||||
{(() => {
|
{(() => {
|
||||||
const isDownloading =
|
const isDownloading =
|
||||||
|
localDownloadingModels.has(
|
||||||
|
variant.id
|
||||||
|
) ||
|
||||||
downloadProcesses.some(
|
downloadProcesses.some(
|
||||||
(e) => e.id === variant.id
|
(e) => e.id === variant.id
|
||||||
)
|
)
|
||||||
@ -607,9 +622,12 @@ function Hub() {
|
|||||||
<div
|
<div
|
||||||
className="size-6 cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out"
|
className="size-6 cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out"
|
||||||
title="Download model"
|
title="Download model"
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
|
addLocalDownloadingModel(
|
||||||
|
variant.id
|
||||||
|
)
|
||||||
downloadModel(variant.id)
|
downloadModel(variant.id)
|
||||||
}
|
}}
|
||||||
>
|
>
|
||||||
<IconDownload
|
<IconDownload
|
||||||
size={16}
|
size={16}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user