From 595c337095ed60c15434bbf634d6410379ddceba Mon Sep 17 00:00:00 2001 From: "Samuele E. Locatelli" Date: Fri, 5 Sep 2025 09:49:23 +0000 Subject: [PATCH] Update gestione modelli lato frontend --- backend/services/redis_service.py | 2 +- frontend/src/App.jsx | 117 +++++++++++++++++++----------- frontend/src/ChatInput.jsx | 40 +++++----- frontend/src/SessionEditor.jsx | 70 ++++++++++++++++++ 4 files changed, 169 insertions(+), 60 deletions(-) create mode 100644 frontend/src/SessionEditor.jsx diff --git a/backend/services/redis_service.py b/backend/services/redis_service.py index b4aa51a..ed2769d 100644 --- a/backend/services/redis_service.py +++ b/backend/services/redis_service.py @@ -67,7 +67,7 @@ def clear_chat(user_id: str, session_id: str): # Session metadata ops # ------------------------- -def create_session(user_id: str, first_message: str) -> dict: +def create_session(user_id: str, first_message: str, model_name: Optional[str] = None) -> dict: session_id = str(uuid.uuid4()) created_at = datetime.utcnow().isoformat() + "Z" session_name = (first_message.strip()[:50] or "New Chat") diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 7b14961..785c1ee 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,19 +1,24 @@ //App.jsx + import React, { useState, useEffect } from "react"; import "./App.css"; import { themes } from "./themes"; import ChatLayout from "./ChatLayout"; import { useChatStream } from "./useChatStream"; import { getSessionId, getUserId, clearSessionId, setSessionId } from "./useSessionId"; -import "katex/dist/katex.min.css"; // <-- IMPORTANTE +import SessionEditor from "./SessionEditor"; +import "katex/dist/katex.min.css"; export default function App() { const { messages, loading, sendMessage, stopGenerating, setMessages } = useChatStream(); const [themeName, setThemeName] = useState("light"); - const [sessionName, setSessionName, sessionModelName ] = useState(""); + const [sessionName, setSessionName] = useState(""); + const [sessionModelName, setSessionModelName] = useState(""); const theme = themes[themeName]; const userId = getUserId(); const sessionId = getSessionId(); + const [showEditor, setShowEditor] = useState(false); + const [editorMode, setEditorMode] = useState("create"); useEffect(() => { const saved = localStorage.getItem("preferredTheme"); @@ -24,10 +29,10 @@ export default function App() { localStorage.setItem("preferredTheme", themeName); }, [themeName]); - // Carica il nome della sessione corrente useEffect(() => { if (!sessionId) { setSessionName(""); + setSessionModelName(""); return; } (async () => { @@ -36,10 +41,12 @@ export default function App() { if (res.ok) { const meta = await res.json(); setSessionName(meta.session_name || ""); + setSessionModelName(meta.model_name || ""); } } catch (err) { - console.error("Errore nel recupero session_name", err); + console.error("Errore nel recupero sessione", err); setSessionName(""); + setSessionModelName(""); } })(); }, [sessionId, userId]); @@ -55,32 +62,43 @@ export default function App() { const freshStart = async () => { await fetch(`/v1/history?user_id=${userId}&session_id=${sessionId}`, { method: "DELETE" }); setMessages([]); - clearSessionId(); // <-- ora rimuove del tutto la sessione + clearSessionId(); setSessionName(""); + setSessionModelName(""); }; - const createSession = async () => { - const firstMessage = prompt("Enter a name or first message for the new session:") || ""; - const res = await fetch(`/v1/sessions?user_id=${userId}`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ first_message: firstMessage }), - }); - const meta = await res.json(); - setSessionId(meta.session_id); - setSessionName(meta.session_name || ""); - setMessages([]); + const createSession = () => { + setEditorMode("create"); + setShowEditor(true); }; - const editSession = async () => { - const newName = prompt("Enter a new name for this session:"); - if (!newName) return; - await fetch(`/v1/sessions/${sessionId}?user_id=${userId}`, { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ session_name: newName }), - }); - setSessionName(newName); + const editSession = () => { + setEditorMode("edit"); + setShowEditor(true); + }; + + const handleEditorConfirm = async ({ session_name, model_name }) => { + if (editorMode === "create") { + const res = await fetch(`/v1/sessions?user_id=${userId}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ first_message: session_name, model_name }) + }); + const meta = await res.json(); + setSessionId(meta.session_id); + setSessionName(meta.session_name || ""); + setSessionModelName(meta.model_name || ""); + setMessages([]); + } else { + await fetch(`/v1/sessions/${sessionId}?user_id=${userId}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ session_name, model_name }) + }); + setSessionName(session_name); + setSessionModelName(model_name || ""); + } + setShowEditor(false); }; const handleSelectSession = async (selectedId) => { @@ -89,23 +107,38 @@ export default function App() { }; return ( - reloadHistory()} - onFreshStart={freshStart} - onCreateSession={createSession} - onEditSession={editSession} - onSelectSession={handleSelectSession} - userId={userId} - sessionId={sessionId} - sessionModelName={sessionModelName} - sessionName={sessionName} - /> + <> + reloadHistory()} + onFreshStart={freshStart} + onCreateSession={createSession} + onEditSession={editSession} + onSelectSession={handleSelectSession} + userId={userId} + sessionId={sessionId} + sessionModelName={sessionModelName} + sessionName={sessionName} + /> + + {showEditor && ( +
+ )} + {showEditor && ( + setShowEditor(false)} + /> + )} + ); } diff --git a/frontend/src/ChatInput.jsx b/frontend/src/ChatInput.jsx index cc5b19d..b99e52e 100644 --- a/frontend/src/ChatInput.jsx +++ b/frontend/src/ChatInput.jsx @@ -1,6 +1,6 @@ // src/ChatInput.jsx + import React, { useState, useEffect, useRef } from "react"; -import axios from "axios"; const MAX_EXECUTION_TIME_MS_DEFAULT = 2 * 60 * 1000; const EXTRA_TIME_MS = 60 * 1000; @@ -26,9 +26,13 @@ export default function ChatInput({ onSend, onStop, loading, sessionModelName = // Fetch models and set default from session useEffect(() => { - axios.get("/v1/models") + fetch("/v1/models") .then(res => { - const sortedModels = res.data.sort((a, b) => a.localeCompare(b)); + if (!res.ok) throw new Error("Network response was not ok"); + return res.json(); + }) + .then(data => { + const sortedModels = data.sort((a, b) => a.localeCompare(b)); setModelList(sortedModels); if (sessionModelName && sortedModels.includes(sessionModelName)) { setSelectedModel(sessionModelName); @@ -54,6 +58,14 @@ export default function ChatInput({ onSend, onStop, loading, sessionModelName = } }, [inputValue, isTextarea]); + useEffect(() => { + if (sessionModelName) { + setSelectedModel(sessionModelName); + } else { + setSelectedModel(""); // fallback al default + } + }, [sessionModelName]); + useEffect(() => { if (loading) { setTimeLeft(Math.floor(maxExecutionTime / 1000)); @@ -130,20 +142,14 @@ export default function ChatInput({ onSend, onStop, loading, sessionModelName = return (
- - + {selectedModel && ( +
+ LLM: {selectedModel.length > 10 ? `${selectedModel.slice(0, 10)}…` : selectedModel} +
+ )} {isTextarea ? (