Merge remote-tracking branch 'upstream/dev' into feat/always-web-search

# Conflicts:
#	src/lib/components/chat/Chat.svelte
This commit is contained in:
hurxxxx
2025-02-05 16:56:34 +09:00
138 changed files with 3549 additions and 1700 deletions
+77 -69
View File
@@ -41,11 +41,13 @@
convertMessagesToHistory,
copyToClipboard,
getMessageContentParts,
createMessagesList,
extractSentencesForAudio,
promptTemplate,
splitStream,
sleep,
removeDetailsWithReasoning
removeDetails,
getPromptVariables
} from '$lib/utils';
import { generateChatCompletion } from '$lib/apis/ollama';
@@ -116,7 +118,7 @@
let selectedToolIds = [];
let imageGenerationEnabled = false;
let webSearchEnabled = $settings?.alwaysOnWebSearch ?? false;
let codeInterpreterEnabled = false;
let chat = null;
let tags = [];
@@ -224,7 +226,7 @@
}
await tick();
saveChatHandler(_chatId);
saveChatHandler(_chatId, history);
};
const chatEventHandler = async (event, cb) => {
@@ -628,7 +630,7 @@
} catch (e) {
// Remove the failed doc from the files array
files = files.filter((f) => f.name !== url);
toast.error(e);
toast.error(`${e}`);
}
};
@@ -825,20 +827,6 @@
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
}
};
const createMessagesList = (responseMessageId) => {
if (responseMessageId === null) {
return [];
}
const message = history.messages[responseMessageId];
if (message?.parentId) {
return [...createMessagesList(message.parentId), message];
} else {
return [message];
}
};
const chatCompletedHandler = async (chatId, modelId, responseMessageId, messages) => {
const res = await chatCompleted(localStorage.token, {
model: modelId,
@@ -895,7 +883,7 @@
};
const chatActionHandler = async (chatId, actionId, modelId, responseMessageId, event = null) => {
const messages = createMessagesList(responseMessageId);
const messages = createMessagesList(history, responseMessageId);
const res = await chatAction(localStorage.token, actionId, {
model: modelId,
@@ -964,7 +952,7 @@
const modelId = selectedModels[0];
const model = $models.filter((m) => m.id === modelId).at(0);
const messages = createMessagesList(history.currentId);
const messages = createMessagesList(history, history.currentId);
const parentMessage = messages.length !== 0 ? messages.at(-1) : null;
const userMessageId = uuidv4();
@@ -1009,9 +997,9 @@
}
if (messages.length === 0) {
await initChatHandler();
await initChatHandler(history);
} else {
await saveChatHandler($chatId);
await saveChatHandler($chatId, history);
}
}
};
@@ -1073,9 +1061,9 @@
}
if (messages.length === 0) {
await initChatHandler();
await initChatHandler(history);
} else {
await saveChatHandler($chatId);
await saveChatHandler($chatId, history);
}
};
@@ -1209,7 +1197,12 @@
);
history.messages[message.id] = message;
await chatCompletedHandler(chatId, message.model, message.id, createMessagesList(message.id));
await chatCompletedHandler(
chatId,
message.model,
message.id,
createMessagesList(history, message.id)
);
}
console.log(data);
@@ -1225,7 +1218,7 @@
const submitPrompt = async (userPrompt, { _raw = false } = {}) => {
console.log('submitPrompt', userPrompt, $chatId);
const messages = createMessagesList(history.currentId);
const messages = createMessagesList(history, history.currentId);
const _selectedModels = selectedModels.map((modelId) =>
$models.map((m) => m.id).includes(modelId) ? modelId : ''
);
@@ -1273,7 +1266,6 @@
}
prompt = '';
await tick();
// Reset chat input textarea
const chatInputElement = document.getElementById('chat-input');
@@ -1315,34 +1307,25 @@
history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
}
// Wait until history/message have been updated
await tick();
// focus on chat input
const chatInput = document.getElementById('chat-input');
chatInput?.focus();
saveSessionSelectedModels();
await sendPrompt(userPrompt, userMessageId, { newChat: true });
await sendPrompt(history, userPrompt, userMessageId, { newChat: true });
};
const sendPrompt = async (
_history,
prompt: string,
parentId: string,
{ modelId = null, modelIdx = null, newChat = false } = {}
) => {
// Create new chat if newChat is true and first user message
if (
newChat &&
history.messages[history.currentId].parentId === null &&
history.messages[history.currentId].role === 'user'
) {
await initChatHandler();
} else {
await saveChatHandler($chatId);
}
let _chatId = JSON.parse(JSON.stringify($chatId));
_history = JSON.parse(JSON.stringify(_history));
const responseMessageIds: Record<PropertyKey, string> = {};
// If modelId is provided, use it, else use selected model
let selectedModelIds = modelId
? [modelId]
@@ -1351,7 +1334,6 @@
: selectedModels;
// Create response messages for each selected model
const responseMessageIds: Record<PropertyKey, string> = {};
for (const [_modelIdx, modelId] of selectedModelIds.entries()) {
const model = $models.filter((m) => m.id === modelId).at(0);
@@ -1386,19 +1368,26 @@
responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`] = responseMessageId;
}
}
history = history;
// Create new chat if newChat is true and first user message
if (newChat && _history.messages[_history.currentId].parentId === null) {
_chatId = await initChatHandler(_history);
}
await tick();
_history = JSON.parse(JSON.stringify(history));
// Save chat after all messages have been created
await saveChatHandler($chatId);
await saveChatHandler(_chatId, _history);
const _chatId = JSON.parse(JSON.stringify($chatId));
await Promise.all(
selectedModelIds.map(async (modelId, _modelIdx) => {
console.log('modelId', modelId);
const model = $models.filter((m) => m.id === modelId).at(0);
if (model) {
const messages = createMessagesList(parentId);
const messages = createMessagesList(_history, parentId);
// If there are image files, check if model is vision capable
const hasImages = messages.some((message) =>
message.files?.some((file) => file.type === 'image')
@@ -1414,7 +1403,7 @@
let responseMessageId =
responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`];
let responseMessage = history.messages[responseMessageId];
let responseMessage = _history.messages[responseMessageId];
let userContext = null;
if ($settings?.memory ?? false) {
@@ -1443,7 +1432,7 @@
const chatEventEmitter = await getChatEventEmitter(model.id, _chatId);
scrollToBottom();
await sendPromptSocket(model, responseMessageId, _chatId);
await sendPromptSocket(_history, model, responseMessageId, _chatId);
if (chatEventEmitter) clearInterval(chatEventEmitter);
} else {
@@ -1456,9 +1445,9 @@
chats.set(await getChatList(localStorage.token, $currentChatPage));
};
const sendPromptSocket = async (model, responseMessageId, _chatId) => {
const responseMessage = history.messages[responseMessageId];
const userMessage = history.messages[responseMessage.parentId];
const sendPromptSocket = async (_history, model, responseMessageId, _chatId) => {
const responseMessage = _history.messages[responseMessageId];
const userMessage = _history.messages[responseMessage.parentId];
let files = JSON.parse(JSON.stringify(chatFiles));
files.push(
@@ -1506,9 +1495,9 @@
}`
}
: undefined,
...createMessagesList(responseMessageId).map((message) => ({
...createMessagesList(_history, responseMessageId).map((message) => ({
...message,
content: removeDetailsWithReasoning(message.content)
content: removeDetails(message.content, ['reasoning', 'code_interpreter'])
}))
]
.filter((message) => message?.content?.trim())
@@ -1559,10 +1548,18 @@
files: (files?.length ?? 0) > 0 ? files : undefined,
tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
features: {
image_generation: imageGenerationEnabled,
code_interpreter: codeInterpreterEnabled,
web_search: webSearchEnabled
},
variables: {
...getPromptVariables(
$user.name,
$settings?.userLocation ? await getAndUpdateUserLocation(localStorage.token) : undefined
)
},
session_id: $socket?.id,
chat_id: $chatId,
@@ -1592,12 +1589,15 @@
},
`${WEBUI_BASE_URL}/api`
).catch((error) => {
console.log(error);
toast.error(`${error}`);
responseMessage.error = {
content: error
};
responseMessage.done = true;
history.messages[responseMessageId] = responseMessage;
history.currentId = responseMessageId;
return null;
});
@@ -1695,7 +1695,7 @@
history.currentId = userMessageId;
await tick();
await sendPrompt(userPrompt, userMessageId);
await sendPrompt(history, userPrompt, userMessageId);
};
const regenerateResponse = async (message) => {
@@ -1707,11 +1707,11 @@
if ((userMessage?.models ?? [...selectedModels]).length == 1) {
// If user message has only one model selected, sendPrompt automatically selects it for regeneration
await sendPrompt(userPrompt, userMessage.id);
await sendPrompt(history, userPrompt, userMessage.id);
} else {
// If there are multiple models selected, use the model of the response message for regeneration
// e.g. many model chat
await sendPrompt(userPrompt, userMessage.id, {
await sendPrompt(history, userPrompt, userMessage.id, {
modelId: message.model,
modelIdx: message.modelIdx
});
@@ -1733,7 +1733,7 @@
.at(0);
if (model) {
await sendPromptSocket(model, responseMessage.id, _chatId);
await sendPromptSocket(history, model, responseMessage.id, _chatId);
}
}
};
@@ -1776,7 +1776,7 @@
}
}
await saveChatHandler(_chatId);
await saveChatHandler(_chatId, history);
} else {
console.error(res);
}
@@ -1785,42 +1785,48 @@
}
};
const initChatHandler = async () => {
const initChatHandler = async (history) => {
let _chatId = $chatId;
if (!$temporaryChatEnabled) {
chat = await createNewChat(localStorage.token, {
id: $chatId,
id: _chatId,
title: $i18n.t('New Chat'),
models: selectedModels,
system: $settings.system ?? undefined,
params: params,
history: history,
messages: createMessagesList(history.currentId),
messages: createMessagesList(history, history.currentId),
tags: [],
timestamp: Date.now()
});
currentChatPage.set(1);
await chats.set(await getChatList(localStorage.token, $currentChatPage));
await chatId.set(chat.id);
_chatId = chat.id;
await chatId.set(_chatId);
window.history.replaceState(history.state, '', `/c/${chat.id}`);
await chats.set(await getChatList(localStorage.token, $currentChatPage));
currentChatPage.set(1);
window.history.replaceState(history.state, '', `/c/${_chatId}`);
} else {
_chatId = 'local';
await chatId.set('local');
}
await tick();
return _chatId;
};
const saveChatHandler = async (_chatId) => {
const saveChatHandler = async (_chatId, history) => {
if ($chatId == _chatId) {
if (!$temporaryChatEnabled) {
chat = await updateChatById(localStorage.token, _chatId, {
models: selectedModels,
history: history,
messages: createMessagesList(history.currentId),
messages: createMessagesList(history, history.currentId),
params: params,
files: chatFiles
});
currentChatPage.set(1);
await chats.set(await getChatList(localStorage.token, $currentChatPage));
}
@@ -1924,7 +1930,7 @@
{/if}
<div class="flex flex-col flex-auto z-10 w-full">
{#if $settings?.landingPageMode === 'chat' || createMessagesList(history.currentId).length > 0}
{#if $settings?.landingPageMode === 'chat' || createMessagesList(history, history.currentId).length > 0}
<div
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden"
id="messages-container"
@@ -1964,6 +1970,7 @@
bind:autoScroll
bind:selectedToolIds
bind:imageGenerationEnabled
bind:codeInterpreterEnabled
bind:webSearchEnabled
bind:atSelectedModel
transparentBackground={$settings?.backgroundImageUrl ?? false}
@@ -2015,6 +2022,7 @@
bind:autoScroll
bind:selectedToolIds
bind:imageGenerationEnabled
bind:codeInterpreterEnabled
bind:webSearchEnabled
bind:atSelectedModel
transparentBackground={$settings?.backgroundImageUrl ?? false}