import { useCallback, useEffect, useMemo, useState } from 'react' import { useDropzone } from 'react-dropzone' import Image from 'next/image' import { InferenceEngine } from '@janhq/core' import { Button, ScrollArea } from '@janhq/joi' import { useAtom, useAtomValue, useSetAtom } from 'jotai' import { ChevronDownIcon, ChevronUpIcon, UploadCloudIcon, UploadIcon, } from 'lucide-react' import { twMerge } from 'tailwind-merge' import BlankState from '@/containers/BlankState' import ModelSearch from '@/containers/ModelSearch' import SetupRemoteModel from '@/containers/SetupRemoteModel' import useDropModelBinaries from '@/hooks/useDropModelBinaries' import { setImportModelStageAtom } from '@/hooks/useImportModel' import { getLogoEngine, getTitleByEngine, isLocalEngine, priorityEngine, } from '@/utils/modelEngine' import MyModelList from './MyModelList' import { extensionManager } from '@/extension' import { downloadedModelsAtom, showEngineListModelAtom, } from '@/helpers/atoms/Model.atom' const MyModels = () => { const downloadedModels = useAtomValue(downloadedModelsAtom) const setImportModelStage = useSetAtom(setImportModelStageAtom) const { onDropModels } = useDropModelBinaries() const [searchText, setSearchText] = useState('') const [showEngineListModel, setShowEngineListModel] = useAtom( showEngineListModelAtom ) const [extensionHasSettings, setExtensionHasSettings] = useState< { name?: string; setting: string; apiKey: string; provider: string }[] >([]) const filteredDownloadedModels = useMemo( () => downloadedModels .filter((e) => e.name.toLowerCase().includes(searchText.toLowerCase().trim()) ) .sort((a, b) => a.name.localeCompare(b.name)), [downloadedModels, searchText] ) const { getRootProps, isDragActive } = useDropzone({ noClick: true, multiple: true, onDrop: onDropModels, }) const onImportModelClick = useCallback(() => { setImportModelStage('SELECTING_MODEL') }, [setImportModelStage]) const onSearchChange = useCallback((input: string) => { setSearchText(input) }, []) useEffect(() => { const getAllSettings = async () => { const extensionsMenu: { name?: string setting: string apiKey: string provider: string }[] = [] const extensions = extensionManager.getAll() for (const extension of extensions) { if (typeof extension.getSettings === 'function') { const settings = await extension.getSettings() if ( (settings && settings.length > 0) || (await extension.installationState()) !== 'NotRequired' ) { extensionsMenu.push({ name: extension.productName, setting: extension.name, apiKey: 'apiKey' in extension && typeof extension.apiKey === 'string' ? extension.apiKey : '', provider: 'provider' in extension && typeof extension.provider === 'string' ? extension.provider : '', }) } } } setExtensionHasSettings(extensionsMenu) } getAllSettings() }, []) const findByEngine = filteredDownloadedModels.map((x) => { // Legacy engine support - they will be grouped under Cortex LlamaCPP if (x.engine === InferenceEngine.nitro) return InferenceEngine.cortex_llamacpp return x.engine }) const groupByEngine = findByEngine .filter(function (item, index) { if (findByEngine.indexOf(item) === index) return item }) .sort((a, b) => { if (priorityEngine.includes(a) && priorityEngine.includes(b)) { return priorityEngine.indexOf(a) - priorityEngine.indexOf(b) } else if (priorityEngine.includes(a)) { return -1 } else if (priorityEngine.includes(b)) { return 1 } else { return 0 // Leave the rest in their original order } }) const getEngineStatusReady: InferenceEngine[] = extensionHasSettings ?.filter((e) => e.apiKey.length > 0) .map((x) => x.provider as InferenceEngine) useEffect(() => { setShowEngineListModel((prev) => [ ...prev, ...(getEngineStatusReady as InferenceEngine[]), ]) // eslint-disable-next-line react-hooks/exhaustive-deps }, [setShowEngineListModel, extensionHasSettings]) return (
{isDragActive && (
Drop file here

File (GGUF) or folder

)}
{!groupByEngine.length ? (
) : ( groupByEngine.map((engine, i) => { const engineLogo = getLogoEngine(engine as InferenceEngine) const showModel = showEngineListModel.includes(engine) const onClickChevron = () => { if (showModel) { setShowEngineListModel((prev) => prev.filter((item) => item !== engine) ) } else { setShowEngineListModel((prev) => [...prev, engine]) } } return (
{engineLogo && ( logo )}
{getTitleByEngine(engine)}
{!isLocalEngine(engine) && ( )} {!showModel ? ( ) : ( )}
{filteredDownloadedModels ? filteredDownloadedModels .filter( (x) => x.engine === engine || (x.engine === InferenceEngine.nitro && engine === InferenceEngine.cortex_llamacpp) ) .map((model) => { if (!showModel) return null return ( ) }) : null}
) }) )}
) } export default MyModels