From cfa68c5500d4c18842890aa734ac7335a7b59074 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Wed, 20 Aug 2025 21:56:53 +0700 Subject: [PATCH] feat: run on startup settin for local api server --- web-app/src/hooks/useLocalApiServer.ts | 2 +- web-app/src/locales/en/settings.json | 3 + web-app/src/providers/DataProvider.tsx | 116 +++++++++++++++++- .../src/routes/settings/local-api-server.tsx | 22 ++++ 4 files changed, 141 insertions(+), 2 deletions(-) diff --git a/web-app/src/hooks/useLocalApiServer.ts b/web-app/src/hooks/useLocalApiServer.ts index 8080d4d2d..7e89bffb8 100644 --- a/web-app/src/hooks/useLocalApiServer.ts +++ b/web-app/src/hooks/useLocalApiServer.ts @@ -33,7 +33,7 @@ type LocalApiServerState = { export const useLocalApiServer = create()( persist( (set) => ({ - runOnStartup: true, + runOnStartup: false, setRunOnStartup: (value) => set({ runOnStartup: value }), serverHost: '127.0.0.1', setServerHost: (value) => set({ serverHost: value }), diff --git a/web-app/src/locales/en/settings.json b/web-app/src/locales/en/settings.json index 4eeb825d5..17e59d3bb 100644 --- a/web-app/src/locales/en/settings.json +++ b/web-app/src/locales/en/settings.json @@ -160,6 +160,9 @@ "serverLogs": "Server Logs", "serverLogsDesc": "View detailed logs of the local API server.", "openLogs": "Open Logs", + "startupConfiguration": "Startup Configuration", + "runOnStartup": "Enable by default on startup", + "runOnStartupDesc": "Automatically start the Local API Server when the application launches.", "serverConfiguration": "Server Configuration", "serverHost": "Server Host", "serverHostDesc": "Network address for the server.", diff --git a/web-app/src/providers/DataProvider.tsx b/web-app/src/providers/DataProvider.tsx index 32c6a374c..cb20a21ce 100644 --- a/web-app/src/providers/DataProvider.tsx +++ b/web-app/src/providers/DataProvider.tsx @@ -17,10 +17,15 @@ import { import { useNavigate } from '@tanstack/react-router' import { route } from '@/constants/routes' import { useThreads } from '@/hooks/useThreads' +import { useLocalApiServer } from '@/hooks/useLocalApiServer' +import { useAppState } from '@/hooks/useAppState' import { AppEvent, events } from '@janhq/core' +import { startModel } from '@/services/models' +import { localStorageKey } from '@/constants/localStorage' export function DataProvider() { - const { setProviders } = useModelProvider() + const { setProviders, selectedModel, selectedProvider, getProviderByName } = + useModelProvider() const { setMessages } = useMessages() const { checkForUpdate } = useAppUpdater() @@ -29,6 +34,19 @@ export function DataProvider() { const { setThreads } = useThreads() const navigate = useNavigate() + // Local API Server hooks + const { + runOnStartup, + serverHost, + serverPort, + apiPrefix, + apiKey, + trustedHosts, + corsEnabled, + verboseLogs, + } = useLocalApiServer() + const { setServerStatus } = useAppState() + useEffect(() => { console.log('Initializing DataProvider...') getProviders().then(setProviders) @@ -78,6 +96,102 @@ export function DataProvider() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []) + const getLastUsedModel = (): { provider: string; model: string } | null => { + try { + const stored = localStorage.getItem(localStorageKey.lastUsedModel) + return stored ? JSON.parse(stored) : null + } catch (error) { + console.debug('Failed to get last used model from localStorage:', error) + return null + } + } + + // Helper function to determine which model to start + const getModelToStart = () => { + // Use last used model if available + const lastUsedModel = getLastUsedModel() + if (lastUsedModel) { + const provider = getProviderByName(lastUsedModel.provider) + if ( + provider && + provider.models.some((m) => m.id === lastUsedModel.model) + ) { + return { model: lastUsedModel.model, provider } + } + } + + // Use selected model if available + if (selectedModel && selectedProvider) { + const provider = getProviderByName(selectedProvider) + if (provider) { + return { model: selectedModel.id, provider } + } + } + + // Use first model from llamacpp provider + const llamacppProvider = getProviderByName('llamacpp') + if ( + llamacppProvider && + llamacppProvider.models && + llamacppProvider.models.length > 0 + ) { + return { + model: llamacppProvider.models[0].id, + provider: llamacppProvider, + } + } + + return null + } + + // Auto-start Local API Server on app startup if enabled + useEffect(() => { + if (runOnStartup) { + // Validate API key before starting + if (!apiKey || apiKey.toString().trim().length === 0) { + console.warn('Cannot start Local API Server: API key is required') + return + } + + const modelToStart = getModelToStart() + + // Only start server if we have a model to load + if (!modelToStart) { + console.warn( + 'Cannot start Local API Server: No model available to load' + ) + return + } + + setServerStatus('pending') + + // Start the model first + startModel(modelToStart.provider, modelToStart.model) + .then(() => { + console.log(`Model ${modelToStart.model} started successfully`) + + // Then start the server + return window.core?.api?.startServer({ + host: serverHost, + port: serverPort, + prefix: apiPrefix, + apiKey, + trustedHosts, + isCorsEnabled: corsEnabled, + isVerboseEnabled: verboseLogs, + }) + }) + .then(() => { + setServerStatus('running') + }) + .catch((error: unknown) => { + console.error('Failed to start Local API Server on startup:', error) + setServerStatus('stopped') + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + const handleDeepLink = (urls: string[] | null) => { if (!urls) return console.log('Received deeplink:', urls) diff --git a/web-app/src/routes/settings/local-api-server.tsx b/web-app/src/routes/settings/local-api-server.tsx index 4461a47a9..7de00f355 100644 --- a/web-app/src/routes/settings/local-api-server.tsx +++ b/web-app/src/routes/settings/local-api-server.tsx @@ -32,6 +32,8 @@ function LocalAPIServer() { setCorsEnabled, verboseLogs, setVerboseLogs, + runOnStartup, + setRunOnStartup, serverHost, serverPort, apiPrefix, @@ -199,6 +201,26 @@ function LocalAPIServer() { /> + {/* Startup Configuration */} + + { + if (!apiKey || apiKey.toString().trim().length === 0) { + setShowApiKeyError(true) + return + } + setRunOnStartup(checked) + }} + /> + } + /> + + {/* Server Configuration */}