Update gestione modelli lato frontend

This commit is contained in:
Samuele E. Locatelli
2025-09-05 09:49:23 +00:00
parent c259266922
commit 595c337095
4 changed files with 169 additions and 60 deletions
+1 -1
View File
@@ -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")
+75 -42
View File
@@ -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 (
<ChatLayout
theme={theme}
messages={messages}
loading={loading}
onSend={sendMessage}
onStop={stopGenerating}
onToggleTheme={toggleTheme}
onReloadHistory={() => reloadHistory()}
onFreshStart={freshStart}
onCreateSession={createSession}
onEditSession={editSession}
onSelectSession={handleSelectSession}
userId={userId}
sessionId={sessionId}
sessionModelName={sessionModelName}
sessionName={sessionName}
/>
<>
<ChatLayout
theme={theme}
messages={messages}
loading={loading}
onSend={sendMessage}
onStop={stopGenerating}
onToggleTheme={toggleTheme}
onReloadHistory={() => reloadHistory()}
onFreshStart={freshStart}
onCreateSession={createSession}
onEditSession={editSession}
onSelectSession={handleSelectSession}
userId={userId}
sessionId={sessionId}
sessionModelName={sessionModelName}
sessionName={sessionName}
/>
{showEditor && (
<div className="modal-backdrop fade show"></div>
)}
{showEditor && (
<SessionEditor
mode={editorMode}
initialName={sessionName}
initialModel={sessionModelName}
onConfirm={handleEditorConfirm}
onCancel={() => setShowEditor(false)}
/>
)}
</>
);
}
+23 -17
View File
@@ -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 (
<div className="chat-input-container p-2 border-top">
<div className="input-group">
<select
className="form-select"
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
style={{ maxWidth: "20%" }}
>
<option value="">Seleziona modello</option>
{modelList.map((model) => (
<option key={model} value={model}>
{model}
</option>
))}
</select>
{selectedModel && (
<div
className="input-group-text small text-muted text-truncate"
title={selectedModel}
style={{ maxWidth: "8rem" }}>
LLM: <strong>{selectedModel.length > 10 ? `${selectedModel.slice(0, 10)}` : selectedModel}</strong>
</div>
)}
{isTextarea ? (
<textarea
ref={inputRef}
+70
View File
@@ -0,0 +1,70 @@
// src/SessionEditor.jsx
import React, { useState, useEffect } from "react";
export default function SessionEditor({ mode = "create", initialName = "", initialModel = "", onConfirm, onCancel }) {
const [sessionName, setSessionName] = useState(initialName);
const [modelName, setModelName] = useState(initialModel);
const [modelList, setModelList] = useState([]);
useEffect(() => {
fetch("/v1/models")
.then(res => res.json())
.then(data => setModelList(data.sort((a, b) => a.localeCompare(b))))
.catch(err => console.error("Errore nel caricamento modelli:", err));
}, []);
const handleSubmit = () => {
if (!sessionName.trim()) return;
onConfirm({ session_name: sessionName, model_name: modelName });
};
return (
<div className="modal fade show d-block" tabIndex="-1" role="dialog">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">
{mode === "edit" ? "Modifica sessione" : "Crea nuova sessione"}
</h5>
<button type="button" className="btn-close" onClick={onCancel}></button>
</div>
<div className="modal-body">
<div className="mb-3">
<label className="form-label">Nome sessione</label>
<input
type="text"
className="form-control"
value={sessionName}
onChange={(e) => setSessionName(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Modello</label>
<select
className="form-select"
value={modelName}
onChange={(e) => setModelName(e.target.value)}
>
<option value="">(Default)</option>
{modelList.map((model) => (
<option key={model} value={model}>
{model}
</option>
))}
</select>
</div>
</div>
<div className="modal-footer">
<button className="btn btn-primary" onClick={handleSubmit}>
Conferma
</button>
<button className="btn btn-secondary" onClick={onCancel}>
Annulla
</button>
</div>
</div>
</div>
</div>
);
}