From e0fa11a4cd1966b3cb487ef23ff4beed96f13f55 Mon Sep 17 00:00:00 2001 From: aditigupta217 Date: Tue, 2 Jun 2026 20:03:21 +0530 Subject: [PATCH] Add clear all sessions functionality --- backend/requirements_fixed.txt | 15 +++++++++++++ backend/routes/sessions.py | 6 +++++ backend/services/db_service.py | 6 +++++ backend/tests/test_api.py | 17 ++++++++++++++ frontend/src/App.jsx | 29 +++++++++++++++++++----- frontend/src/components/Sidebar.jsx | 18 ++++++++++++++- frontend/src/utils/api.js | 35 +++++++++++++++-------------- 7 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 backend/requirements_fixed.txt diff --git a/backend/requirements_fixed.txt b/backend/requirements_fixed.txt new file mode 100644 index 0000000..38f6283 --- /dev/null +++ b/backend/requirements_fixed.txt @@ -0,0 +1,15 @@ +fastapi==0.115.0 +uvicorn[standard]==0.30.0 +langchain==0.3.0 +langchain-community==0.3.0 +ollama==0.3.0 +chromadb==0.5.3 +sentence-transformers==3.0.0 +pypdf==4.3.0 +python-docx==1.1.2 +python-multipart==0.0.9 +pydantic==2.9.0 +python-dotenv==1.0.1 +httpx==0.27.0 +pytest==8.3.0 +pytest-asyncio==0.24.0 diff --git a/backend/routes/sessions.py b/backend/routes/sessions.py index 8b6091e..002551e 100644 --- a/backend/routes/sessions.py +++ b/backend/routes/sessions.py @@ -118,3 +118,9 @@ async def rag_stats(session_id: str): count = 0 return {"session_id": session_id, "indexed_chunks": count} + +@router.delete("/") +async def clear_all_sessions(): + db_service.clear_all_sessions() + return {"message": "All sessions cleared"} + diff --git a/backend/services/db_service.py b/backend/services/db_service.py index dd33814..d1a1889 100644 --- a/backend/services/db_service.py +++ b/backend/services/db_service.py @@ -153,6 +153,12 @@ def delete_session(session_id: str): conn.execute("DELETE FROM sessions WHERE id=?", (session_id,)) +def clear_all_sessions(): + with get_db() as conn: + conn.execute("DELETE FROM messages") + conn.execute("DELETE FROM sessions") + + def get_all_sessions() -> list[dict]: with get_db() as conn: rows = conn.execute( diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 2b83e30..fb389a2 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -222,6 +222,7 @@ def test_export_txt(): assert r2.status_code == 200 assert b"Plain text export" in r2.content + # ─── Prompt Templates ──────────────────────────────────────── def test_create_prompt_template(): r = client.post("/api/prompt-templates/", json={ @@ -281,3 +282,19 @@ def test_create_prompt_template_empty_title(): "prompt": "Some prompt" }) assert r.status_code == 422 + +def test_clear_all_sessions(): + r1 = client.post("/api/sessions/", json={"title": "Session 1"}) + r2 = client.post("/api/sessions/", json={"title": "Session 2"}) + assert r1.status_code == 200 + assert r2.status_code == 200 + + r_delete = client.delete("/api/sessions/") + assert r_delete.status_code == 200 + assert r_delete.json() == {"message": "All sessions cleared"} + + r_list = client.get("/api/sessions/") + assert r_list.status_code == 200 + assert len(r_list.json()) == 0 + + diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 73529f0..7e99049 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -53,19 +53,24 @@ export default function App() { if (settRes.value.default_language) setLanguage(settRes.value.default_language); } if (stRes.status === "fulfilled") setOllamaOk(stRes.value.ollama_running); - } catch {} + } catch { } } const refreshSessions = useCallback(async () => { - try { const s = await api.getSessions(); setSessions(s || []); } catch {} + try { const s = await api.getSessions(); setSessions(s || []); } catch { } }, []); const refreshDocuments = useCallback(async (sid) => { - try { const d = await api.getDocuments(sid); setDocuments(d.documents || []); } catch {} + try { const d = await api.getDocuments(sid); setDocuments(d.documents || []); } catch { } }, []); async function sendMessage(text) { if (!text.trim() || loading || streaming) return; + let activeSid = sessionId; + if (!activeSid) { + activeSid = uuidv4(); + setSessionId(activeSid); + } const userMsg = { role: "user", content: text, id: Date.now() }; setMessages(prev => [...prev, userMsg]); @@ -75,7 +80,7 @@ export default function App() { setMessages(prev => [...prev, aiMsg]); try { await api.streamMessage( - { message: text, session_id: sessionId, model, use_documents: documents.length > 0, language }, + { message: text, session_id: activeSid, model, use_documents: documents.length > 0, language }, (token) => setMessages(prev => prev.map(m => m.id === aiMsg.id ? { ...m, content: m.content + token } : m)), (sources) => { setMessages(prev => prev.map(m => m.id === aiMsg.id ? { ...m, sources, streaming: false } : m)); @@ -88,7 +93,7 @@ export default function App() { } else { setLoading(true); try { - const data = await api.sendMessage({ message: text, session_id: sessionId, model, use_documents: documents.length > 0, language }); + const data = await api.sendMessage({ message: text, session_id: activeSid, model, use_documents: documents.length > 0, language }); setMessages(prev => [...prev, { role: "assistant", content: data.reply, sources: data.sources || [], id: Date.now() + 1 }]); refreshSessions(); } catch (e) { @@ -116,7 +121,7 @@ export default function App() { const [msgRes, docRes] = await Promise.all([api.getMessages(sid), api.getDocuments(sid)]); setMessages((msgRes.messages || []).map((m, i) => ({ ...m, id: i }))); setDocuments(docRes.documents || []); - } catch {} + } catch { } } async function handleDeleteSession(sid) { @@ -125,6 +130,17 @@ export default function App() { refreshSessions(); } + async function handleClearAllSessions() { + try { + await api.clearAllSessions(); + setSessions([]); + setSessionId(null); + setMessages([]); + setDocuments([]); + setPanel(null); + } catch { } + } + async function handleClearChat() { await api.clearMessages(sessionId); setMessages([]); @@ -138,6 +154,7 @@ export default function App() { onNewChat={newChat} onLoadSession={loadSession} onDeleteSession={handleDeleteSession} + onClearAllSessions={handleClearAllSessions} model={model} models={models} onModelChange={setModel} diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx index 4927201..fcfa6f6 100644 --- a/frontend/src/components/Sidebar.jsx +++ b/frontend/src/components/Sidebar.jsx @@ -8,7 +8,7 @@ const LANGUAGES = [ {code:"de",label:"Deutsch"},{code:"es",label:"Español"}, ]; -export default function Sidebar({ sessions, currentSession, onNewChat, onLoadSession, onDeleteSession, model, models, onModelChange, language, onLanguageChange }) { +export default function Sidebar({ sessions, currentSession, onNewChat, onLoadSession, onDeleteSession, onClearAllSessions, model, models, onModelChange, language, onLanguageChange }) { const [search, setSearch] = useState(""); const modelList = models.length > 0 ? models.map(m=>m.name) : ["llama3","mistral","phi3","gemma2"]; const filtered = sessions.filter(s => s.title?.toLowerCase().includes(search.toLowerCase())); @@ -83,6 +83,22 @@ export default function Sidebar({ sessions, currentSession, onNewChat, onLoadSes ))} + {sessions.length > 0 && ( +
+ +
+ )} + {/* Footer */}

diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js index 53ba1b9..b0be1fd 100644 --- a/frontend/src/utils/api.js +++ b/frontend/src/utils/api.js @@ -12,21 +12,22 @@ async function req(path, opts = {}) { return res.json(); } -export const sendMessage = (b) => req("/chat/", { method: "POST", body: JSON.stringify(b) }); -export const getSessions = () => req("/sessions/"); -export const createSession = (b) => req("/sessions/", { method: "POST", body: JSON.stringify(b) }); -export const updateSession = (id,b) => req(`/sessions/${id}`, { method: "PATCH", body: JSON.stringify(b) }); -export const deleteSession = (id) => req(`/sessions/${id}`, { method: "DELETE" }); -export const getMessages = (id) => req(`/sessions/${id}/messages`); -export const clearMessages = (id) => req(`/sessions/${id}/messages`, { method: "DELETE" }); -export const getDocuments = (id) => req(`/sessions/${id}/documents`); -export const getModels = () => req("/models/"); -export const getOllamaStatus= () => req("/models/status"); -export const getPlugins = () => req("/plugins/"); -export const runPlugin = (b) => req("/plugins/run", { method: "POST", body: JSON.stringify(b) }); -export const getSettings = () => req("/settings/"); -export const saveSettings = (b) => req("/settings/", { method: "PUT", body: JSON.stringify(b) }); -export const exportSession = (id, fmt) => window.open(`${BASE}/export/${id}/${fmt}`, "_blank"); +export const sendMessage = (b) => req("/chat/", { method: "POST", body: JSON.stringify(b) }); +export const getSessions = () => req("/sessions/"); +export const createSession = (b) => req("/sessions/", { method: "POST", body: JSON.stringify(b) }); +export const updateSession = (id, b) => req(`/sessions/${id}`, { method: "PATCH", body: JSON.stringify(b) }); +export const deleteSession = (id) => req(`/sessions/${id}`, { method: "DELETE" }); +export const clearAllSessions = () => req("/sessions/", { method: "DELETE" }); +export const getMessages = (id) => req(`/sessions/${id}/messages`); +export const clearMessages = (id) => req(`/sessions/${id}/messages`, { method: "DELETE" }); +export const getDocuments = (id) => req(`/sessions/${id}/documents`); +export const getModels = () => req("/models/"); +export const getOllamaStatus = () => req("/models/status"); +export const getPlugins = () => req("/plugins/"); +export const runPlugin = (b) => req("/plugins/run", { method: "POST", body: JSON.stringify(b) }); +export const getSettings = () => req("/settings/"); +export const saveSettings = (b) => req("/settings/", { method: "PUT", body: JSON.stringify(b) }); +export const exportSession = (id, fmt) => window.open(`${BASE}/export/${id}/${fmt}`, "_blank"); export const deleteDocument = (docId) => req(`/upload/${docId}`, { method: "DELETE" }); // Prompt Templates @@ -39,7 +40,7 @@ export async function uploadDocument(file, session_id) { const fd = new FormData(); fd.append("file", file); fd.append("session_id", session_id); const res = await fetch(`${BASE}/upload/`, { method: "POST", body: fd }); - if (!res.ok) { const e = await res.json().catch(()=>({})); throw new Error(e.detail||"Upload failed"); } + if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.detail || "Upload failed"); } return res.json(); } @@ -54,7 +55,7 @@ export function streamMessage(body, onToken, onDone) { if (done) return; decoder.decode(value).split("\n").forEach(line => { if (line.startsWith("data: ")) { - try { const d = JSON.parse(line.slice(6)); if (d.token) onToken(d.token); if (d.done) onDone(d.sources||[]); } catch {} + try { const d = JSON.parse(line.slice(6)); if (d.token) onToken(d.token); if (d.done) onDone(d.sources || []); } catch { } } }); return pump();