diff --git a/README.md b/README.md index 6f186f24bd9e9d301942082c0dcc1bba04d7fa98..22015384e4dcb9b9a0604e406c5b222f78f1045c 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,30 @@ cp ./.env.template.local ./.env.production.local - [RAGChat-API](https://gitlab.rrz.uni-hamburg.de/zbhai/ragchat-api) - [PM2](https://pm2.keymetrics.io/) +# Routes +- [ ] check routes + - [ ] AUTH + - [ ] register + - [ ] confirm register + - [ ] resend register token + - [ ] login + - [ ] renew JWT + - [ ] logout + - [ ] request password reset + - [ ] password reset + - [ ] AI + - [ ] status + - [ ] get models + - [ ] get model + - [ ] install model [admin only] + - [ ] delete model [admin only] + - [ ] chat + - [ ] list chats + - [ ] EMBEDDINGS + - [ ] delete vector db [admin only] + - [ ] get vector db [admin only] + - [ ] update embeddings [admin only] + # Roadmap - [ ] complete pages - [ ] resend verification code @@ -29,5 +53,4 @@ cp ./.env.template.local ./.env.production.local - [ ] admin-login - [ ] admin-page with LLM options - [ ] fix errors - - [ ] fix axios' JWT auto renewal - [ ] check width of label & submit on cleanLayout \ No newline at end of file diff --git a/README_tmp.html b/README_tmp.html index dca7ce2f8632d1df4cb41ace219df8acd580d714..1632be88b58fe6e46e72490235c972651d6f2565 100644 --- a/README_tmp.html +++ b/README_tmp.html @@ -387,20 +387,56 @@ cp ./.env.template.local ./.env.production.local <li><a href="https://gitlab.rrz.uni-hamburg.de/zbhai/ragchat-api">RAGChat-API</a></li> <li><a href="https://pm2.keymetrics.io/">PM2</a></li> </ul> +<h1 id="routes">Routes</h1> +<ul> +<li><input type="checkbox" id="checkbox0"><label for="checkbox0">check routes</label> +<ul> +<li><input type="checkbox" id="checkbox1"><label for="checkbox1">AUTH</label> +<ul> +<li><input type="checkbox" id="checkbox2"><label for="checkbox2">register</label></li> +<li><input type="checkbox" id="checkbox3"><label for="checkbox3">confirm register</label></li> +<li><input type="checkbox" id="checkbox4"><label for="checkbox4">resend register token</label></li> +<li><input type="checkbox" id="checkbox5"><label for="checkbox5">login</label></li> +<li><input type="checkbox" id="checkbox6"><label for="checkbox6">renew JWT</label></li> +<li><input type="checkbox" id="checkbox7"><label for="checkbox7">logout</label></li> +<li><input type="checkbox" id="checkbox8"><label for="checkbox8">request password reset</label></li> +<li><input type="checkbox" id="checkbox9"><label for="checkbox9">password reset</label></li> +</ul> +</li> +<li><input type="checkbox" id="checkbox10"><label for="checkbox10">AI</label> +<ul> +<li><input type="checkbox" id="checkbox11"><label for="checkbox11">status</label></li> +<li><input type="checkbox" id="checkbox12"><label for="checkbox12">get models</label></li> +<li><input type="checkbox" id="checkbox13"><label for="checkbox13">get model</label></li> +<li><input type="checkbox" id="checkbox14"><label for="checkbox14">install model [admin only]</label></li> +<li><input type="checkbox" id="checkbox15"><label for="checkbox15">delete model [admin only]</label></li> +<li><input type="checkbox" id="checkbox16"><label for="checkbox16">chat</label></li> +<li><input type="checkbox" id="checkbox17"><label for="checkbox17">list chats</label></li> +</ul> +</li> +<li><input type="checkbox" id="checkbox18"><label for="checkbox18">EMBEDDINGS</label> +<ul> +<li><input type="checkbox" id="checkbox19"><label for="checkbox19">delete vector db [admin only]</label></li> +<li><input type="checkbox" id="checkbox20"><label for="checkbox20">get vector db [admin only]</label></li> +<li><input type="checkbox" id="checkbox21"><label for="checkbox21">update embeddings [admin only]</label></li> +</ul> +</li> +</ul> +</li> +</ul> <h1 id="roadmap">Roadmap</h1> <ul> -<li><input type="checkbox" id="checkbox0"><label for="checkbox0">complete pages</label> +<li><input type="checkbox" id="checkbox22"><label for="checkbox22">complete pages</label> <ul> -<li><input type="checkbox" id="checkbox1"><label for="checkbox1">resend verification code</label></li> -<li><input type="checkbox" id="checkbox2"><label for="checkbox2">onboarding / RAGChat</label></li> -<li><input type="checkbox" id="checkbox3"><label for="checkbox3">admin-login</label></li> -<li><input type="checkbox" id="checkbox4"><label for="checkbox4">admin-page with LLM options</label></li> +<li><input type="checkbox" id="checkbox23"><label for="checkbox23">resend verification code</label></li> +<li><input type="checkbox" id="checkbox24"><label for="checkbox24">onboarding / RAGChat</label></li> +<li><input type="checkbox" id="checkbox25"><label for="checkbox25">admin-login</label></li> +<li><input type="checkbox" id="checkbox26"><label for="checkbox26">admin-page with LLM options</label></li> </ul> </li> -<li><input type="checkbox" id="checkbox5"><label for="checkbox5">fix errors</label> +<li><input type="checkbox" id="checkbox27"><label for="checkbox27">fix errors</label> <ul> -<li><input type="checkbox" id="checkbox6"><label for="checkbox6">fix axios' JWT auto renewal</label></li> -<li><input type="checkbox" id="checkbox7"><label for="checkbox7">check width of label & submit on cleanLayout</label></li> +<li><input type="checkbox" id="checkbox28"><label for="checkbox28">check width of label & submit on cleanLayout</label></li> </ul> </li> </ul> diff --git a/src/components/chat/Chats.jsx b/src/components/chat/Chats.jsx index 3a117d07153bcd7683d4e60aab3e8ccec138542a..3936679222403a696b12448e6c38d2cfa9c7ac5d 100644 --- a/src/components/chat/Chats.jsx +++ b/src/components/chat/Chats.jsx @@ -4,6 +4,7 @@ import Heading from '../font/Heading'; import { RiAddCircleLine, RiArrowLeftCircleLine, RiArrowRightCircleLine } from 'react-icons/ri'; import { useChat } from '../../contexts/Chat/ChatState'; import { useParams } from 'react-router-dom'; +import { mergeBackendValidation } from '../../utils/ErrorHandling'; const Chats = () => { // ################################# @@ -40,7 +41,7 @@ const Chats = () => { // on unmount abort request controller.abort(); }; - }, []); + }, [currentChatId]); // ################################# @@ -52,7 +53,7 @@ const Chats = () => { // ################################# return ( <div className="row-start-2 row-span-2 border-r-2 border-UhhGrey flex flex-col"> - <Heading level="6" className="text-center">Recent</Heading> + <Heading level="6" className="text-center mx-2">Recent</Heading> {<button onClick={() => { selectChat(null); }} disabled={currentChatId ? false : true} className='text-UhhBlue disabled:text-UhhLightBlue' title='start a new chat'><RiAddCircleLine /></button>} @@ -63,7 +64,7 @@ const Chats = () => { key={chat.id} id={chat.id} title={chat.title} - time={new Intl.DateTimeFormat(import.meta.env.VITE_LOCALE).format(new Date(chat.created))} + time={new Intl.DateTimeFormat(import.meta.env.VITE_LOCALE).format(new Date(chat.createdAt))} /> )) } diff --git a/src/components/chat/PromptInput.jsx b/src/components/chat/PromptInput.jsx index 8aaedecc460dad019a3a4866544af4a46daf792a..3561f2d78223bd06e9ddf7fe60351d4123192790 100644 --- a/src/components/chat/PromptInput.jsx +++ b/src/components/chat/PromptInput.jsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import React from 'react'; +import React, { useEffect } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { RiLoopRightFill, RiSendPlane2Line } from 'react-icons/ri'; import { z } from 'zod'; @@ -23,7 +23,7 @@ function PromptInput() { // HOOKS // ################################# // ### CONNECT CONTEXT - const { currentChatId, availableModels, updateChatHistory } = useChat(); + const { fetchAllModels, currentChatId, availableModels, updateChatHistory, fetchAllChats } = useChat(); // ### PREPARE FORM const methods = useForm({ @@ -31,10 +31,32 @@ function PromptInput() { mode: 'onSubmit', defaultValues: { input: '', - model: 'llama2' + model: '' } }); + // ### FETCH MODELS; + useEffect(() => { + // ### on run exec this code + const controller = new AbortController(); + + const getModels = async () => { + try { + // fetch all chats + await fetchAllModels(); + } catch (error) { + mergeBackendValidation(error.response.status, error.response.data); + } + }; + getModels(); + + // ### return will be executed on unmounting this component + return () => { + // on unmount abort request + controller.abort(); + }; + }, []); + // ################################# // FUNCTIONS // ################################# @@ -46,17 +68,25 @@ function PromptInput() { try { // send input to api const result = await api.post('/ai/chat', inputs); + console.log("🚀 ~ handleSendForm ~ result id:", result.data.chat.id); + + if (!currentChatId) fetchAllChats(result.data.chat.id); + + // update chat history - // BUG if currentChatId is null visible update fails - updateChatHistory(currentChatId, result.data.chat.chatHistory); + updateChatHistory(result.data.chat.id, result.data.chat.chatHistory); + // selectChat(currentChatId); // clear input field methods.resetField('input'); } catch (error) { console.error(error); // merge front & backend validation errors + console.log("🚀 ~ handleSendForm ~ error:", error); mergeBackendValidation(error.response.status, error.response.data, methods.setError); } + + } // TODO width of input field on large screens should increase @@ -64,10 +94,10 @@ function PromptInput() { // OUTPUT // ################################# return ( - <div className="row-start-3 p-3 border-t-2 border-UhhGrey"> + <div className="row-start-3 p-3 border-t-2 border-UhhGrey text-center"> <FormProvider {...methods} > <form onSubmit={methods.handleSubmit(handleSendForm)}> - <div className="flex content-center h-14 relative"> + <div className="flex justify-center content-center h-14 relative"> {methods.formState.isSubmitting && <div className='absolute bg-white bg-opacity-60 z-10 h-full w-full flex items-center justify-center text-2xl'><RiLoopRightFill className='animate-spin' /></div>} <Select name="model" options={availableModels} /> diff --git a/src/contexts/Chat/ChatReducer.jsx b/src/contexts/Chat/ChatReducer.jsx index 773f2af7cca91a7252d8779dd80bf3ec869f1d4b..d0472f283c305c6a3a4097b602fc010682d13d17 100644 --- a/src/contexts/Chat/ChatReducer.jsx +++ b/src/contexts/Chat/ChatReducer.jsx @@ -19,7 +19,7 @@ const chatReducer = (state, action) => { } case CHAT_ACTIONS.SET_MODELS: { - const models = action.payload.data; + const models = action.payload.models; const modelNames = models.map(model => { return { title: model.name, _id: model.name }; }); diff --git a/src/contexts/Chat/ChatState.jsx b/src/contexts/Chat/ChatState.jsx index 4e9fdaf86e83113c239ea2553b9e421fb06cafd4..57a61d4f690076b9ec963dc91084fa0ef3d3593e 100644 --- a/src/contexts/Chat/ChatState.jsx +++ b/src/contexts/Chat/ChatState.jsx @@ -37,10 +37,11 @@ function ChatState({ children }) { // select chat if id is provided if (id) selectChat(id); // fetch available models - const models = await api.post('/ai/models', { filter: '' }); - dispatchAvailableModels({ type: CHAT_ACTIONS.SET_MODELS, payload: models }); + // const models = await api.post('/ai/models', { filter: '' }); + // dispatchAvailableModels({ type: CHAT_ACTIONS.SET_MODELS, payload: models }); } catch (error) { // display errors + console.error(error); mergeBackendValidation(error.response.status, error.response.data); } } @@ -74,9 +75,11 @@ function ChatState({ children }) { const isSelectedChatId = element => element.id === id; // get index of matching item const index = chats.findIndex(isSelectedChatId); - chats[index].chatHistory = history; - - dispatchChatHistory({ type: CHAT_ACTIONS.SET_HISTORY, payload: { id, chats } }); + // update chat history + if (index > -1) { + chats[index].chatHistory = history; + dispatchChatHistory({ type: CHAT_ACTIONS.SET_HISTORY, payload: { id, chats } }); + } } @@ -87,6 +90,19 @@ function ChatState({ children }) { // save chat id in state dispatchCurrentChatId({ type: CHAT_ACTIONS.UPDATE_CHATID, payload: id }); } + + // ### FETCH MODELS + async function fetchAllModels(id = null) { + try { + // fetch available models + const response = await api.post('/ai/models', { filter: '' }); + dispatchAvailableModels({ type: CHAT_ACTIONS.SET_MODELS, payload: response.data }); + } catch (error) { + // display errors + console.error(error); + mergeBackendValidation(error.response.status, error.response.data); + } + } // ################################# // OUTPUT // ################################# @@ -100,7 +116,8 @@ function ChatState({ children }) { fetchChatHistory, chatHistory, availableModels, - updateChatHistory + updateChatHistory, + fetchAllModels }}> {children} </ChatContext.Provider> diff --git a/src/utils/AxiosConfig.js b/src/utils/AxiosConfig.js index 8478be0cb700cb507b6d87bd6f7ff13d28392dda..0fb806aede0c0c0669565665ba55823e73d7bd9c 100755 --- a/src/utils/AxiosConfig.js +++ b/src/utils/AxiosConfig.js @@ -49,7 +49,6 @@ api.interceptors.response.use( try { const result = await api.get( '/auth', - {}, { withCredentials: true } ); // TODO: don't store accessToken in localStorage, keep in memory only