import { useCallback, useEffect } 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 { 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 { totalRamAtom, usedRamAtom } from '@/helpers/atoms/SystemBar.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, startModel } = useActiveModel() const [serverEnabled, setServerEnabled] = useAtom(serverEnabledAtom) const { setMainViewState } = useMainViewState() const [openAISettings, setOpenAISettings] = useState< { api_key: string } | undefined >(undefined) const { readOpenAISettings, saveOpenAISettings } = useEngineSettings() const totalRam = useAtomValue(totalRamAtom) const usedRam = useAtomValue(usedRamAtom) useEffect(() => { readOpenAISettings().then((settings) => { setOpenAISettings(settings) }) // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const { recommendedModel, downloadedModels } = useRecommendedModel() const [first, setfirst] = useState(false) const selectedName = downloadedModels.filter((x) => x.id === selectedModel?.id)[0]?.name ?? '' /** * 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(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) } }, [ recommendedModel, activeThread, setSelectedModel, setThreadModelParams, threadStates, ]) const onValueSelected = useCallback( (modelId: string) => { const model = downloadedModels.find((m) => m.id === modelId) setSelectedModel(model) if (activeModel?.id !== modelId) { startModel(modelId) } 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, setSelectedModel, setThreadModelParams, ] ) if (!activeThread) { return null } return ( <> {selectedName}
Local
) : ( {downloadedModels.map((x, i) => (
{x.name}
{toGibibytes(x.metadata.size)} {x.engine == InferenceEngine.nitro && ( )}
{downloadedModels.length === 0 ? (

{`Oops, you don't have a model yet.`}

) : ( {downloadedModels.map((x, i) => (
{x.name}
{toGibibytes(x.metadata.size)} {x.engine == InferenceEngine.nitro && getLabel(x.metadata.size)}
))}
)}
{selected?.engine === InferenceEngine.openai && (
{ saveOpenAISettings({ apiKey: e.target.value }) }} />
)}
) }