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 ModelSearch from '@/containers/ModelSearch' import SetupRemoteModel from '@/containers/SetupRemoteModel' import useDropModelBinaries from '@/hooks/useDropModelBinaries' import { setImportModelStageAtom } from '@/hooks/useImportModel' import { getLogoEngine, getTitleByEngine, localEngines, 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) => 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 (
File (GGUF) or folder