From 33cc0b8e137b9b1a8f8141b366208f522ed3b54c Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Sat, 3 Jan 2026 18:05:33 +0100 Subject: [PATCH] fix(chat/ui): record model name in history for consistency (#7845) Signed-off-by: Ettore Di Giacinto --- core/http/static/chat.js | 60 ++++++++++++++++++++++++++++++--------- core/http/views/chat.html | 47 +++++++++++++++++++++++++----- 2 files changed, 86 insertions(+), 21 deletions(-) diff --git a/core/http/static/chat.js b/core/http/static/chat.js index 203ac5f2e..6c74a9c1c 100644 --- a/core/http/static/chat.js +++ b/core/http/static/chat.js @@ -750,6 +750,7 @@ function stopRequest() { if (!activeChat) return; const request = activeRequests.get(activeChat.id); + const requestModel = request?.model || null; // Get model before deleting request if (request) { if (request.controller) { request.controller.abort(); @@ -779,7 +780,8 @@ function stopRequest() { `Request cancelled by user`, null, null, - activeChat.id + activeChat.id, + requestModel ); } @@ -1231,7 +1233,8 @@ async function promptGPT(systemPrompt, input) { startTime: requestStartTime, tokensReceived: 0, interval: null, - maxTokensPerSecond: 0 + maxTokensPerSecond: 0, + model: model // Store the model used for this request }); // Update reactive tracking for UI indicators @@ -1271,21 +1274,27 @@ async function promptGPT(systemPrompt, input) { return; } else { // Timeout error (controller was aborted by timeout, not user) + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; chatStore.add( "assistant", `Request timeout: MCP processing is taking longer than expected. Please try again.`, null, null, - chatId + chatId, + requestModel ); } } else { + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; chatStore.add( "assistant", `Network Error: ${error.message}`, null, null, - chatId + chatId, + requestModel ); } toggleLoader(false, chatId); @@ -1299,12 +1308,15 @@ async function promptGPT(systemPrompt, input) { } if (!response.ok) { + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; chatStore.add( "assistant", `Error: POST ${endpoint} ${response.status}`, null, null, - chatId + chatId, + requestModel ); toggleLoader(false, chatId); activeRequests.delete(chatId); @@ -1324,12 +1336,15 @@ async function promptGPT(systemPrompt, input) { .getReader(); if (!reader) { + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; chatStore.add( "assistant", `Error: Failed to decode MCP API response`, null, null, - chatId + chatId, + requestModel ); toggleLoader(false, chatId); activeRequests.delete(chatId); @@ -1598,12 +1613,15 @@ async function promptGPT(systemPrompt, input) { break; case "error": + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; chatStore.add( "assistant", `MCP Error: ${eventData.message}`, null, null, - chatId + chatId, + requestModel ); break; } @@ -1624,9 +1642,11 @@ async function promptGPT(systemPrompt, input) { // Update or create assistant message with processed regular content const currentChat = chatStore.getChat(chatId); if (!currentChat) break; // Chat was deleted + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; if (lastAssistantMessageIndex === -1) { if (processedRegular && processedRegular.trim()) { - chatStore.add("assistant", processedRegular, null, null, chatId); + chatStore.add("assistant", processedRegular, null, null, chatId, requestModel); lastAssistantMessageIndex = targetHistory.length - 1; } } else { @@ -1706,7 +1726,9 @@ async function promptGPT(systemPrompt, input) { lastMessage.html = DOMPurify.sanitize(marked.parse(lastMessage.content)); } } else if (processedRegular && processedRegular.trim()) { - chatStore.add("assistant", processedRegular, null, null, chatId); + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; + chatStore.add("assistant", processedRegular, null, null, chatId, requestModel); lastAssistantMessageIndex = targetHistory.length - 1; } } @@ -1754,7 +1776,9 @@ async function promptGPT(systemPrompt, input) { lastMessage.html = DOMPurify.sanitize(marked.parse(lastMessage.content)); } } else { - chatStore.add("assistant", finalRegular, null, null, chatId); + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; + chatStore.add("assistant", finalRegular, null, null, chatId, requestModel); } } @@ -1812,12 +1836,15 @@ async function promptGPT(systemPrompt, input) { .getReader(); if (!reader) { + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; chatStore.add( "assistant", `Error: Failed to decode API response`, null, null, - chatId + chatId, + requestModel ); toggleLoader(false, chatId); activeRequests.delete(chatId); @@ -1848,9 +1875,11 @@ async function promptGPT(systemPrompt, input) { const addToChat = (token) => { const currentChat = chatStore.getChat(chatId); if (!currentChat) return; // Chat was deleted - chatStore.add("assistant", token, null, null, chatId); - // Count tokens for rate calculation (per chat) + // Get model from request for this chat const request = activeRequests.get(chatId); + const requestModel = request?.model || null; + chatStore.add("assistant", token, null, null, chatId, requestModel); + // Count tokens for rate calculation (per chat) if (request) { const tokenCount = Math.ceil(token.length / 4); request.tokensReceived += tokenCount; @@ -2008,12 +2037,15 @@ async function promptGPT(systemPrompt, input) { if (error.name !== 'AbortError' || !currentAbortController) { const currentChat = chatStore.getChat(chatId); if (currentChat) { + const request = activeRequests.get(chatId); + const requestModel = request?.model || null; chatStore.add( "assistant", `Error: Failed to process stream`, null, null, - chatId + chatId, + requestModel ); } } diff --git a/core/http/views/chat.html b/core/http/views/chat.html index e733ebe10..dbb4dc862 100644 --- a/core/http/views/chat.html +++ b/core/http/views/chat.html @@ -276,12 +276,31 @@ SOFTWARE. } }, - add(role, content, image, audio, targetChatId = null) { + add(role, content, image, audio, targetChatId = null, model = null) { // If targetChatId is provided, add to that chat, otherwise use active chat // This allows streaming to continue to the correct chat even if user switches const chat = targetChatId ? this.getChat(targetChatId) : this.activeChat(); if (!chat) return; + // Determine model for this message: + // - If model is explicitly provided, use it (for assistant messages with specific model) + // - For user messages, use the current chat's model + // - For other messages (thinking, tool_call, etc.), inherit from previous message or use chat model + let messageModel = model; + if (!messageModel) { + if (role === "user") { + // User messages always use the current chat's model + messageModel = chat.model || ""; + } else if (role === "assistant") { + // Assistant messages use the chat's model (should be set when request is made) + messageModel = chat.model || ""; + } else { + // For thinking, tool_call, etc., try to inherit from last assistant message, or use chat model + const lastAssistant = chat.history.slice().reverse().find(m => m.role === "assistant"); + messageModel = lastAssistant?.model || chat.model || ""; + } + } + const N = chat.history.length - 1; // For thinking, reasoning, tool_call, and tool_result messages, always create a new message if (role === "thinking" || role === "reasoning" || role === "tool_call" || role === "tool_result") { @@ -311,7 +330,7 @@ SOFTWARE. // Reasoning, tool_call, and tool_result are always collapsed by default const isMCPMode = chat.mcpMode || false; const shouldExpand = (role === "thinking" && !isMCPMode) || false; - chat.history.push({ role, content, html: c, image, audio, expanded: shouldExpand }); + chat.history.push({ role, content, html: c, image, audio, expanded: shouldExpand, model: messageModel }); // Auto-name chat from first user message if (role === "user" && chat.name === "New Chat" && content.trim()) { @@ -332,6 +351,10 @@ SOFTWARE. if (audio && audio.length > 0) { chat.history[N].audio = [...(chat.history[N].audio || []), ...audio]; } + // Preserve model if merging (don't overwrite) + if (!chat.history[N].model && messageModel) { + chat.history[N].model = messageModel; + } } else { let c = ""; const lines = content.split("\n"); @@ -343,7 +366,8 @@ SOFTWARE. content, html: c, image: image || [], - audio: audio || [] + audio: audio || [], + model: messageModel }); // Auto-name chat from first user message @@ -1248,11 +1272,20 @@ SOFTWARE.