import { useCallback, useEffect, useState } from 'react' import { InferenceEngine, Model } from '@janhq/core' import { Button, Select, SelectContent, SelectGroup, SelectPortal, SelectItem, SelectTrigger, SelectValue, } from '@janhq/uikit' import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai' import { MonitorIcon } from 'lucide-react' import { twMerge } from 'tailwind-merge' import { MainViewState } from '@/constants/screens' import { useActiveModel } from '@/hooks/useActiveModel' import { useMainViewState } from '@/hooks/useMainViewState' import useRecommendedModel from '@/hooks/useRecommendedModel' import { toGibibytes } from '@/utils/converter' import ModelLabel from '../ModelLabel' import OpenAiKeyInput from '../OpenAiKeyInput' import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom' import { ModelParams, activeThreadAtom, getActiveThreadIdAtom, setThreadModelParamsAtom, threadStatesAtom, } from '@/helpers/atoms/Thread.atom' export const selectedModelAtom = atom(undefined) export default function DropdownListSidebar() { const activeThreadId = useAtomValue(getActiveThreadIdAtom) const activeThread = useAtomValue(activeThreadAtom) const threadStates = useAtomValue(threadStatesAtom) const [selectedModel, setSelectedModel] = useAtom(selectedModelAtom) const setThreadModelParams = useSetAtom(setThreadModelParamsAtom) const { activeModel, stateModel } = useActiveModel() const [serverEnabled, setServerEnabled] = useAtom(serverEnabledAtom) const { setMainViewState } = useMainViewState() const { recommendedModel, downloadedModels } = useRecommendedModel() /** * Default value for max_tokens and ctx_len * Its to avoid OOM issue since a model can set a big number for these settings */ const defaultValue = (value?: number) => { if (value && value < 4096) return value return 4096 } useEffect(() => { setSelectedModel(selectedModel || activeModel || recommendedModel) if (activeThread) { const finishInit = threadStates[activeThread.id].isFinishInit ?? true if (finishInit) return const modelParams: ModelParams = { ...recommendedModel?.parameters, ...recommendedModel?.settings, /** * This is to set default value for these settings instead of maximum value * Should only apply when model.json has these settings */ ...(recommendedModel?.parameters.max_tokens && { max_tokens: defaultValue(recommendedModel?.parameters.max_tokens), }), ...(recommendedModel?.settings.ctx_len && { ctx_len: defaultValue(recommendedModel?.settings.ctx_len), }), } setThreadModelParams(activeThread.id, modelParams) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [ recommendedModel, activeThread, setSelectedModel, setThreadModelParams, threadStates, ]) const [loader, setLoader] = useState(0) // This is fake loader please fix this when we have realtime percentage when load model useEffect(() => { if (stateModel.loading) { if (loader === 24) { setTimeout(() => { setLoader(loader + 1) }, 250) } else if (loader === 50) { setTimeout(() => { setLoader(loader + 1) }, 250) } else if (loader === 78) { setTimeout(() => { setLoader(loader + 1) }, 250) } else if (loader === 85) { setLoader(85) } else { setLoader(loader + 1) } } else { setLoader(0) } }, [stateModel.loading, loader]) const onValueSelected = useCallback( async (modelId: string) => { const model = downloadedModels.find((m) => m.id === modelId) setSelectedModel(model) if (serverEnabled) { window.core?.api?.stopServer() setServerEnabled(false) } if (activeThreadId) { const modelParams = { ...model?.parameters, ...model?.settings, } setThreadModelParams(activeThreadId, modelParams) } }, // eslint-disable-next-line react-hooks/exhaustive-deps [ downloadedModels, serverEnabled, activeThreadId, activeModel, setThreadModelParams, ] ) if (!activeThread) { return null } return ( <>
) }