import { useCallback, useMemo } from 'react' import { ModelSource } from '@janhq/core' import { Button, Tooltip, Dropdown, Badge } from '@janhq/joi' import { useAtomValue, useSetAtom } from 'jotai' import { ChevronDownIcon } from 'lucide-react' import ModalCancelDownload from '@/containers/ModalCancelDownload' import { MainViewState } from '@/constants/screens' import { useCreateNewThread } from '@/hooks/useCreateNewThread' import useDownloadModel from '@/hooks/useDownloadModel' import { useSettings } from '@/hooks/useSettings' import { toGigabytes } from '@/utils/converter' import { extractModelName } from '@/utils/modelSource' import { fuzzySearch } from '@/utils/search' import { mainViewStateAtom } from '@/helpers/atoms/App.atom' import { assistantsAtom } from '@/helpers/atoms/Assistant.atom' import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom' import { downloadedModelsAtom, getDownloadingModelAtom, } from '@/helpers/atoms/Model.atom' import { selectedSettingAtom } from '@/helpers/atoms/Setting.atom' import { nvidiaTotalVramAtom, totalRamAtom, } from '@/helpers/atoms/SystemBar.atom' type Props = { model: ModelSource onSelectedModel: () => void } const ModelItemHeader = ({ model, onSelectedModel }: Props) => { const { downloadModel } = useDownloadModel() const downloadingModels = useAtomValue(getDownloadingModelAtom) const downloadedModels = useAtomValue(downloadedModelsAtom) const setSelectedSetting = useSetAtom(selectedSettingAtom) const { requestCreateNewThread } = useCreateNewThread() const totalRam = useAtomValue(totalRamAtom) const { settings } = useSettings() const nvidiaTotalVram = useAtomValue(nvidiaTotalVramAtom) const setMainViewState = useSetAtom(mainViewStateAtom) // Default nvidia returns vram in MB, need to convert to bytes to match the unit of totalRamW let ram = nvidiaTotalVram * 1024 * 1024 if (ram === 0 || settings?.gpus?.some((gpu) => gpu.activated !== true)) { ram = totalRam } const serverEnabled = useAtomValue(serverEnabledAtom) const assistants = useAtomValue(assistantsAtom) const onDownloadClick = useCallback(() => { downloadModel(model.models?.[0].id) }, [model, downloadModel]) const isDownloaded = downloadedModels.some((md) => model.models.some((m) => m.id === md.id) ) const defaultModel = useMemo(() => { return model.models?.find( (e) => e.id.includes('q4-km') || fuzzySearch('q4km', e.id) ) }, [model]) let downloadButton = (
Download
({ name: (
{e.id} {e.id === defaultModel?.id && ( Default )}
), value: e.id, suffix: toGigabytes(e.size), }))} onValueChanged={(e) => downloadModel(e)} >
) const isDownloading = downloadingModels.some((md) => model.models.some((m) => m.id === md) ) const onUseModelClick = useCallback(async () => { const downloadedModel = downloadedModels.find((e) => model.models.some((m) => m.id === e.id) ) if (downloadedModel) { await requestCreateNewThread(assistants[0], downloadedModel) setMainViewState(MainViewState.Thread) } }, [ assistants, model, requestCreateNewThread, setMainViewState, downloadedModels, ]) if (isDownloaded) { downloadButton = ( Use } disabled={!serverEnabled} content="Threads are disabled while the server is running" /> ) } else if (isDownloading) { downloadButton = ( model.models.some((m) => m.id === e)) ?? model.id } /> ) } return (
{extractModelName(model.metadata?.id)}
{toGigabytes(model.models?.[0]?.size)}
{model.type !== 'cloud' ? ( downloadButton ) : ( <> {!model.metadata?.apiKey?.length && ( )} )}
) } export default ModelItemHeader