Update gestione modelli lato frontend
This commit is contained in:
@@ -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
@@ -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
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user