'use client' import React, { useCallback, useEffect, useMemo, useState } from 'react' import ScrollToBottom from 'react-scroll-to-bottom' import { Button, Switch, Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipTrigger, Select, SelectContent, SelectItem, Input, SelectTrigger, SelectValue, } from '@janhq/uikit' import { useAtom, useAtomValue, useSetAtom } from 'jotai' import { Paintbrush, CodeIcon } from 'lucide-react' import { ExternalLinkIcon, InfoIcon } from 'lucide-react' import { AlertTriangleIcon } from 'lucide-react' import { twMerge } from 'tailwind-merge' import CardSidebar from '@/containers/CardSidebar' import DropdownListSidebar, { selectedModelAtom, } from '@/containers/DropdownListSidebar' import ModalTroubleShooting, { modalTroubleShootingAtom, } from '@/containers/ModalTroubleShoot' import ServerLogs from '@/containers/ServerLogs' import { toaster } from '@/containers/Toast' import { loadModelErrorAtom, useActiveModel } from '@/hooks/useActiveModel' import { useLogs } from '@/hooks/useLogs' import { getConfigurationsData } from '@/utils/componentSettings' import { toRuntimeParams, toSettingParams } from '@/utils/modelParam' import EngineSetting from '../Chat/EngineSetting' import ModelSetting from '../Chat/ModelSetting' import { showRightSideBarAtom } from '../Chat/Sidebar' import { apiServerCorsEnabledAtom, apiServerHostAtom, apiServerPortAtom, apiServerPrefix, apiServerVerboseLogEnabledAtom, hostOptions, } from '@/helpers/atoms/ApiServer.atom' import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom' const LocalServerScreen = () => { const [errorRangePort, setErrorRangePort] = useState(false) const [errorPrefix, setErrorPrefix] = useState(false) const [serverEnabled, setServerEnabled] = useAtom(serverEnabledAtom) const showRightSideBar = useAtomValue(showRightSideBarAtom) const setModalTroubleShooting = useSetAtom(modalTroubleShootingAtom) const { openServerLog, clearServerLog } = useLogs() const { startModel, stateModel } = useActiveModel() const selectedModel = useAtomValue(selectedModelAtom) const modelRuntimeParams = toRuntimeParams(selectedModel?.settings) const [currentModelSettingParams, setCurrentModelSettingParams] = useState( toSettingParams(selectedModel?.settings) ) const componentDataEngineSetting = getConfigurationsData( currentModelSettingParams ) const componentDataRuntimeSetting = getConfigurationsData( modelRuntimeParams, selectedModel ) const [isCorsEnabled, setIsCorsEnabled] = useAtom(apiServerCorsEnabledAtom) const [isVerboseEnabled, setIsVerboseEnabled] = useAtom( apiServerVerboseLogEnabledAtom ) const [host, setHost] = useAtom(apiServerHostAtom) const [port, setPort] = useAtom(apiServerPortAtom) const [prefix, setPrefix] = useAtom(apiServerPrefix) const [loadModelError, setLoadModelError] = useAtom(loadModelErrorAtom) const FIRST_TIME_VISIT_API_SERVER = 'firstTimeVisitAPIServer' const [firstTimeVisitAPIServer, setFirstTimeVisitAPIServer] = useState(false) const handleChangePort = useCallback( (value: string) => { setErrorRangePort(Number(value) <= 0 || Number(value) >= 65536) setPort(value) }, [setPort] ) const handleChangePrefix = useCallback( (value: string) => { setErrorPrefix(!value.length || !value.startsWith('/')) setPrefix(value) }, [setPrefix] ) useEffect(() => { if (localStorage.getItem(FIRST_TIME_VISIT_API_SERVER) == null) { setFirstTimeVisitAPIServer(true) } }, [firstTimeVisitAPIServer]) useEffect(() => { handleChangePort(port) }, [handleChangePort, port]) useEffect(() => { handleChangePrefix(prefix) }, [handleChangePrefix, prefix]) const engineSettings = useMemo( () => componentDataEngineSetting.filter((x) => x.key !== 'prompt_template'), [componentDataEngineSetting] ) const modelSettings = useMemo(() => { return componentDataRuntimeSetting.filter( (x) => x.key !== 'prompt_template' ) }, [componentDataRuntimeSetting]) const onStartServerClick = async () => { if (selectedModel == null) return try { const isStarted = await window.core?.api?.startServer({ host, port, prefix, isCorsEnabled, isVerboseEnabled, }) await startModel(selectedModel.id) if (isStarted) setServerEnabled(true) if (firstTimeVisitAPIServer) { localStorage.setItem(FIRST_TIME_VISIT_API_SERVER, 'false') setFirstTimeVisitAPIServer(false) } } catch (e) { console.error(e) toaster({ title: `Failed to start server!`, description: 'Please check Server Logs for more details.', type: 'error', }) } } const onStopServerClick = async () => { window.core?.api?.stopServer() setServerEnabled(false) setLoadModelError(undefined) } const onToggleServer = async () => { if (serverEnabled) { await onStopServerClick() } else { await onStartServerClick() } } const onValueChanged = useCallback( (key: string, value: string | number | boolean) => { setCurrentModelSettingParams({ ...currentModelSettingParams, [key]: value, }) }, [currentModelSettingParams] ) return (
{/* Left SideBar */}

Server Options

Start an OpenAI-compatible local HTTP server.

{serverEnabled && ( )}

Server Options

{ handleChangePort(e.target.value) }} maxLength={5} disabled={serverEnabled} />
{errorRangePort && (

{`The port range should be from 0 to 65536`}

)}
{ handleChangePrefix(e.target.value) }} disabled={serverEnabled} />
{errorPrefix && (

{`Prefix should start with /`}

)}
setIsCorsEnabled(e)} name="cors" disabled={serverEnabled} />
setIsVerboseEnabled(e)} name="verbose" disabled={serverEnabled} />
{serverEnabled && ( Settings cannot be modified while the server is running )}
{/* Middle Bar */}

Server Logs

{firstTimeVisitAPIServer ? (
Once you start the server, you cannot chat with your assistant.
) : (
)}
{/* Right bar */}

You can concurrently send requests to one active local model and multiple remote models.

{loadModelError && serverEnabled && (
Model failed to start. Access{' '} setModalTroubleShooting(true)} > troubleshooting assistance
)} {modelSettings.length !== 0 && (
)} {engineSettings.length !== 0 && (
)}
) } export default LocalServerScreen