diff --git a/src/db/schema.ts b/src/db/schema.ts index 9eefa55..094852d 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -17,3 +17,14 @@ export const chats = sqliteTable('chats', { createdAt: text('createdAt').notNull(), focusMode: text('focusMode').notNull(), }); + +export const settings = sqliteTable('settings', { + id: integer('id').primaryKey(), + chatModelProvider: text('chatModelProvider'), + chatModel: text('chatModel'), + embeddingModelProvider: text('embeddingModelProvider'), + embeddingModel: text('embeddingModel'), + openAIApiKey: text('openAIApiKey'), + openAIBaseURL: text('openAIBaseURL'), + // TODO: add user auth +}); \ No newline at end of file diff --git a/src/routes/index.ts b/src/routes/index.ts index af928ab..a9a0264 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -5,6 +5,7 @@ import configRouter from './config'; import modelsRouter from './models'; import suggestionsRouter from './suggestions'; import chatsRouter from './chats'; +import settingsRouter from './settings'; const router = express.Router(); @@ -14,5 +15,6 @@ router.use('/config', configRouter); router.use('/models', modelsRouter); router.use('/suggestions', suggestionsRouter); router.use('/chats', chatsRouter); +router.use('/settings', settingsRouter); export default router; diff --git a/src/routes/settings.ts b/src/routes/settings.ts new file mode 100644 index 0000000..6400286 --- /dev/null +++ b/src/routes/settings.ts @@ -0,0 +1,43 @@ +import express from 'express'; +import db from '../db'; +import { settings, eq } from '../db/schema'; +const router = express.Router(); + +router.post('/', async (req, res) => { + try { + const { chatModelProvider, chatModel, embeddingModelProvider, embeddingModel, openAIApiKey, openAIBaseURL } = req.body; + // TODO: Add user authentication + + await db.delete(settings).execute(); + + await db.insert(settings).values({ + chatModelProvider, + chatModel, + embeddingModelProvider, + embeddingModel, + openAIApiKey, + openAIBaseURL, + }).execute(); + + res.status(200).json({ message: 'Settings saved successfully' }); + } catch (err) { + console.error('Error saving settings:', err); + res.status(500).json({ message: 'An error occurred while saving settings' }); + } +}); + +router.get('/', async (req, res) => { + try { + // TODO: Add user authentication + const userSettings = await db.query.settings.findFirst(); + if (!userSettings) { + return res.status(404).json({ message: 'No settings found' }); + } + res.status(200).json(userSettings); + } catch (err) { + console.error('Error fetching settings:', err); + res.status(500).json({ message: 'An error occurred while fetching settings' }); + } +}); + +export default router; \ No newline at end of file diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index ea9a93d..74df3b2 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -21,6 +21,25 @@ export type Message = { sources?: Document[]; }; + +const fetchSettings = async () => { + try { + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/settings`); + const settings = await res.json(); + + if (settings) { + localStorage.setItem('chatModelProvider', settings.chatModelProvider); + localStorage.setItem('chatModel', settings.chatModel); + localStorage.setItem('embeddingModelProvider', settings.embeddingModelProvider); + localStorage.setItem('embeddingModel', settings.embeddingModel); + localStorage.setItem('openAIApiKey', settings.openAIApiKey); + localStorage.setItem('openAIBaseURL', settings.openAIBaseURL); + } + } catch (err) { + console.error('Failed to fetch settings:', err); + } +}; + const useSocket = ( url: string, setIsWSReady: (ready: boolean) => void, @@ -31,6 +50,8 @@ const useSocket = ( useEffect(() => { if (!ws) { const connectWs = async () => { + await fetchSettings(); + let chatModel = localStorage.getItem('chatModel'); let chatModelProvider = localStorage.getItem('chatModelProvider'); let embeddingModel = localStorage.getItem('embeddingModel'); @@ -324,6 +345,9 @@ const ChatWindow = ({ id }: { id?: string }) => { const messagesRef = useRef([]); + useEffect(() => { + fetchSettings(); + }, []); useEffect(() => { messagesRef.current = messages; }, [messages]); diff --git a/ui/components/SettingsDialog.tsx b/ui/components/SettingsDialog.tsx index 171e812..5ac6c52 100644 --- a/ui/components/SettingsDialog.tsx +++ b/ui/components/SettingsDialog.tsx @@ -146,31 +146,35 @@ const SettingsDialog = ({ const handleSubmit = async () => { setIsUpdating(true); - + try { - await fetch(`${process.env.NEXT_PUBLIC_API_URL}/config`, { + await fetch(`${process.env.NEXT_PUBLIC_API_URL}/settings`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify(config), + body: JSON.stringify({ + chatModelProvider: selectedChatModelProvider, + chatModel: selectedChatModel, + embeddingModelProvider: selectedEmbeddingModelProvider, + embeddingModel: selectedEmbeddingModel, + openAIApiKey: customOpenAIApiKey, + openAIBaseURL: customOpenAIBaseURL, + }), }); - + + // Still keep localStorage for quick access on the client-side localStorage.setItem('chatModelProvider', selectedChatModelProvider!); localStorage.setItem('chatModel', selectedChatModel!); - localStorage.setItem( - 'embeddingModelProvider', - selectedEmbeddingModelProvider!, - ); + localStorage.setItem('embeddingModelProvider', selectedEmbeddingModelProvider!); localStorage.setItem('embeddingModel', selectedEmbeddingModel!); localStorage.setItem('openAIApiKey', customOpenAIApiKey!); localStorage.setItem('openAIBaseURL', customOpenAIBaseURL!); } catch (err) { - console.log(err); + console.error(err); } finally { setIsUpdating(false); setIsOpen(false); - window.location.reload(); } }; diff --git a/ui/components/Sidebar.tsx b/ui/components/Sidebar.tsx index cc2097d..db6b13e 100644 --- a/ui/components/Sidebar.tsx +++ b/ui/components/Sidebar.tsx @@ -1,5 +1,4 @@ 'use client'; - import { cn } from '@/lib/utils'; import { BookOpenText, Home, Search, SquarePen, Settings } from 'lucide-react'; import Link from 'next/link'; @@ -79,13 +78,13 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => { -
+
{navLinks.map((link, i) => ( {

{link.label}

))} +
{children}