diff --git a/package.json b/package.json index ed4b893e..b939de55 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "node": ">=18.0.0" }, "scripts": { + "start": "npm run open-browser && vite", "dev": "npm run open-browser && vite dev", "open-browser": "start http://localhost:5015/", "build": "vite build", diff --git a/src/lib/common/Breadcrumb.svelte b/src/lib/common/Breadcrumb.svelte index bb590189..438bcc74 100644 --- a/src/lib/common/Breadcrumb.svelte +++ b/src/lib/common/Breadcrumb.svelte @@ -13,7 +13,7 @@
@@ -159,6 +161,7 @@ class="form-control" id="user-password" placeholder="Enter password" + disabled={isSubmitting} aria-label="Password" aria-describedby="password-addon" bind:value={password} @@ -167,6 +170,7 @@ color="light" type="button" id="password-addon" + disabled={isSubmitting} on:click={() => onPasswordToggle()} > @@ -179,6 +183,7 @@ class="form-check-input" type="checkbox" id="remember-check" + disabled={isSubmitting} bind:checked={isRememberMe} /> @@ -200,26 +205,38 @@ {/if} {#if PUBLIC_AUTH_ENABLE_FIND_PWD == 'true' }
- + Forgot your password?
@@ -231,7 +248,7 @@

© {new Date().getFullYear()} diff --git a/src/routes/(home)/+page.svelte b/src/routes/(home)/+page.svelte index a5ab0ebc..25f1fd8d 100644 --- a/src/routes/(home)/+page.svelte +++ b/src/routes/(home)/+page.svelte @@ -42,7 +42,7 @@ {#if showHomeImage}

- +
{/if} diff --git a/src/routes/VerticalLayout/Sidebar.svelte b/src/routes/VerticalLayout/Sidebar.svelte index 5ddd1264..0abbeaa2 100644 --- a/src/routes/VerticalLayout/Sidebar.svelte +++ b/src/routes/VerticalLayout/Sidebar.svelte @@ -107,7 +107,6 @@ let activeLinks = 0; const links = menu.nextElementSibling.querySelectorAll('li a'); links.forEach((x) => (activeLinks += Number(x.classList.contains('mm-active')))); - console.log(activeLinks) if (activeLinks > 0) { menu.classList.add('mm-active'); } diff --git a/src/routes/chat/[agentId]/+page.svelte b/src/routes/chat/[agentId]/+page.svelte index 4c3f84ed..d3301b40 100644 --- a/src/routes/chat/[agentId]/+page.svelte +++ b/src/routes/chat/[agentId]/+page.svelte @@ -8,6 +8,7 @@ import { getToken, setToken } from '$lib/services/auth-service.js' import { getUserStore } from '$lib/helpers/store.js'; import { conversationStore, getConversationStore } from '$lib/helpers/store.js'; + import { LERNER_ID, TRAINING_MODE } from '$lib/helpers/constants'; const params = $page.params; @@ -19,11 +20,12 @@ onMount(async () => { let user = getUserStore(); - if (user.token) { - console.log("login as existing account."); - } else if($page.url.searchParams.has('token')) { + if ($page.url.searchParams.has('token')) { let token = $page.url.searchParams.get('token'); - setToken(token); + console.log("login as explicit token in query."); + await setToken(token); + } else if (user.token) { + console.log("login as existing account."); } else { await getToken("guest@gmail.com", "123456", () => { console.log("login as guest."); @@ -39,10 +41,21 @@ conversationId = conversation.id; let chatUrl = `chat/${agentId}/${conversationId}`; + let query = ""; + + if (agentId === LERNER_ID) { + query += `mode=${TRAINING_MODE}`; + } + const isFrame = $page.url.searchParams.get('isFrame'); if (isFrame === 'true') { - chatUrl = `${chatUrl}?isFrame=true` + query += "isFrame=true"; + } + + if (!!query) { + chatUrl = `${chatUrl}?${query}`; } + window.location.href = chatUrl; }); diff --git a/src/routes/chat/[agentId]/[conversationId]/agent-info/chat-agent-info.svelte b/src/routes/chat/[agentId]/[conversationId]/agent-info/chat-agent-info.svelte new file mode 100644 index 00000000..80e0f5a5 --- /dev/null +++ b/src/routes/chat/[agentId]/[conversationId]/agent-info/chat-agent-info.svelte @@ -0,0 +1,42 @@ + + +
+
+ + +

directToAgentPage(agent?.id)} + > + {agent?.name || ''} +

+
+
+
+ {agent?.description || ''} +
+
+
+
+ {agent?.llm_config?.provider || ''}{!!agent?.llm_config?.provider ? ',': ''} {agent?.llm_config?.model || ''} +
+
+
+
+ {#if !!agent?.profiles} + {agent?.profiles?.length || 0} {agent?.profiles?.length > 1 ? 'profiles' : 'profile'}{', '} + {/if} + {#if !!agent?.functions} + {agent?.functions?.length || 0} {agent?.functions?.length > 1 ? 'functions' : 'function'}{', '} + {/if} + {#if !!agent?.utilities} + {agent?.utilities?.length || 0} {agent?.utilities?.length > 1 ? 'utilities' : 'utility'} + {/if} +
+
+
\ No newline at end of file diff --git a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte index 654949c9..4826398b 100644 --- a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte +++ b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte @@ -16,7 +16,8 @@ GetDialogs, deleteConversationMessage, getConversationFiles, - getConversationUser + getConversationUser, + uploadConversationFiles } from '$lib/services/conversation-service.js'; import 'overlayscrollbars/overlayscrollbars.css'; import { OverlayScrollbars } from 'overlayscrollbars'; @@ -24,7 +25,7 @@ import { onMount, setContext, tick } from 'svelte'; import Viewport from 'svelte-viewport-info'; import { PUBLIC_LIVECHAT_ENTRY_ICON } from '$env/static/public'; - import { BOT_SENDERS, FILE_EDITORS, TEXT_EDITORS, USER_SENDERS } from '$lib/helpers/constants'; + import { BOT_SENDERS, LERNER_ID, TEXT_EDITORS, TRAINING_MODE, USER_SENDERS } from '$lib/helpers/constants'; import { signalr } from '$lib/services/signalr-service.js'; import { webSpeech } from '$lib/services/web-speech.js'; import { newConversation } from '$lib/services/conversation-service'; @@ -35,17 +36,18 @@ import ChatTextArea from '$lib/common/ChatTextArea.svelte'; import { utcToLocal } from '$lib/helpers/datetime'; import { replaceNewLine } from '$lib/helpers/http'; - import { EditorType, SenderAction, UserRole } from '$lib/helpers/enums'; - import RichContent from './richContent/rich-content.svelte'; - import RcMessage from "./richContent/rc-message.svelte"; - import RcDisclaimer from './richContent/rc-disclaimer.svelte'; + import { EditorType, FileSourceType, SenderAction, UserRole } from '$lib/helpers/enums'; + import { loadFileGallery } from '$lib/helpers/utils/gallery'; + import RichContent from './rich-content/rich-content.svelte'; + import RcMessage from "./rich-content/rc-message.svelte"; + import RcDisclaimer from './rich-content/rc-disclaimer.svelte'; import MessageImageGallery from '$lib/common/MessageImageGallery.svelte'; - import ChatImageUploader from './chatImage/chat-image-uploader.svelte'; - import ChatImageGallery from './chatImage/chat-image-gallery.svelte'; - import ContentLog from './contentLogs/content-log.svelte'; + import ChatImageUploader from './chat-image/chat-image-uploader.svelte'; + import ChatImageGallery from './chat-image/chat-image-gallery.svelte'; + import PersistLog from './persist-log/persist-log.svelte'; + import InstantLog from './instant-log/instant-log.svelte'; import _ from "lodash"; import { Pane, Splitpanes } from 'svelte-splitpanes'; - import StateLog from './stateLogs/state-log.svelte'; import Swal from 'sweetalert2/dist/sweetalert2.js'; import "sweetalert2/src/sweetalert2.scss"; import moment from 'moment'; @@ -112,10 +114,10 @@ let conversationUser; /** @type {boolean} */ - let isLoadContentLog = false; - let isLoadStateLog = false; - let isContentLogClosed = false; // initial condition - let isStateLogClosed = false; // initial condition + let isLoadPersistLog = false; + let isLoadInstantLog = false; + let isPersistLogClosed = false; // initial condition + let isInstantLogClosed = false; // initial condition let isOpenEditMsgModal = false; let isOpenUserAddStateModal = false; let isSendingMsg = false; @@ -124,16 +126,23 @@ let isFrame = false; let loadEditor = false; let loadTextEditor = false; - let loadFileEditor = false; + let loadFileEditor = true; let autoScrollLog = false; + let disableAction = false; + + /** @type {string} */ + let mode = ''; $: { const editor = lastBotMsg?.rich_content?.editor || ''; loadTextEditor = TEXT_EDITORS.includes(editor) || !Object.values(EditorType).includes(editor); - loadFileEditor = FILE_EDITORS.includes(editor); loadEditor = !isSendingMsg && !isThinking && (loadTextEditor || loadFileEditor); } + $: { + disableAction = currentUser?.role !== UserRole.Admin && currentUser?.id !== conversationUser?.id; + } + setContext('chat-window-context', { autoScrollToBottom: autoScrollToBottom }); @@ -166,11 +175,11 @@ function resizeChatWindow() { isLite = Viewport.Width <= screenWidthThreshold; if (!isLite) { - isLoadContentLog = !isContentLogClosed; - isLoadStateLog = !isStateLogClosed; + isLoadPersistLog = !isPersistLogClosed; + isLoadInstantLog = !isInstantLogClosed; } else { - isLoadContentLog = false; - isLoadStateLog = false; + isLoadPersistLog = false; + isLoadInstantLog = false; isOpenEditMsgModal = false; isOpenUserAddStateModal = false; } @@ -178,9 +187,10 @@ function initChatView() { isFrame = $page.url.searchParams.get('isFrame') === 'true'; + mode = $page.url.searchParams.get('mode') || ''; // initial condition - isContentLogClosed = false; - isStateLogClosed = false; + isPersistLogClosed = false; + isInstantLogClosed = false; resizeChatWindow(); } @@ -244,7 +254,6 @@ // trigger UI render dialogs = dialogs?.map(item => { return { ...item }; }) || []; lastBotMsg = findLastBotMessage(dialogs); - assignLoadImageMessages(dialogs); assignMessageDisclaimer(dialogs) groupedDialogs = groupDialogs(dialogs); await tick(); @@ -260,24 +269,6 @@ }) } - /** @param {import('$types').ChatResponseModel[]} dialogs */ - function assignLoadImageMessages(dialogs) { - if (!!!dialogs) return; - - for (let idx = 0; idx < dialogs.length; idx++) { - const curMsg = dialogs[idx]; - if (!USER_SENDERS.includes(curMsg?.sender?.role || '')) { - continue; - } - - const prevMsg = dialogs[idx-1]; - if (!!prevMsg && BOT_SENDERS.includes(prevMsg?.sender?.role || '') - && prevMsg?.rich_content?.editor === EditorType.File) { - curMsg.is_load_images = true; - } - } - } - /** @param {import('$types').ChatResponseModel[]} dialogs */ function assignMessageDisclaimer(dialogs) { if (!!!dialogs) return null; @@ -318,10 +309,6 @@ } function getChatFiles() { - if (lastBotMsg?.rich_content?.editor !== EditorType.File) { - return []; - } - const attachments = conversationUserAttachmentStore.get(); return attachments?.accepted_files || []; } @@ -329,34 +316,43 @@ /** @param {import('$types').ChatResponseModel} message */ function onMessageReceivedFromClient(message) { - dialogs.push(message); + dialogs.push({ + ...message, + is_chat_message: true + }); refresh(); text = ""; } /** @param {import('$types').ChatResponseModel} message */ function onMessageReceivedFromCsr(message) { - dialogs.push(message); + dialogs.push({ + ...message, + is_chat_message: true + }); refresh(); } /** @param {import('$types').ChatResponseModel} message */ function onMessageReceivedFromAssistant(message) { // webSpeech.utter(message.text); - dialogs.push(message); + dialogs.push({ + ...message, + is_chat_message: true + }); refresh(); } /** @param {import('$types').ConversationContentLogModel} log */ function onConversationContentLogGenerated(log) { - if (!isLoadContentLog) return; + if (!isLoadPersistLog) return; contentLogs.push({ ...log }); contentLogs = contentLogs.map(x => { return { ...x }; }); } /** @param {import('$types').ConversationStateLogModel} log */ function onConversationStateLogGenerated(log) { - if (!isLoadStateLog) return; + if (!isLoadPersistLog) return; convStateLogs.push({ ...log }); convStateLogs = convStateLogs.map(x => { return { ...x }; }); @@ -364,7 +360,7 @@ /** @param {import('$types').MessageStateLogModel} log */ function onStateChangeGenerated(log) { - if (!isLoadStateLog || log == null) return; + if (!isLoadInstantLog || log == null) return; msgStateLogs.push({ ...log }); msgStateLogs = msgStateLogs.map(x => { return { ...x }; }); @@ -372,7 +368,7 @@ /** @param {import('$types').AgentQueueLogModel} log */ function onAgentQueueChanged(log) { - if (!isLoadContentLog || log == null) return; + if (!isLoadInstantLog || log == null) return; agentQueueLogs.push({ ...log }); agentQueueLogs = agentQueueLogs.map(x => { return { ...x }; }); @@ -402,6 +398,9 @@ window.location.href = url; } + function handleSaveKnowledge() { + sendChatMessage("Save knowledge"); + } /** * @param {string} msgText @@ -414,29 +413,44 @@ renewUserSentMessages(msgText); const postback = buildPostbackMessage(dialogs, data?.payload || msgText, data?.truncateMsgId); /** @type {import('$types').MessageData?} */ - const messageData = { + let messageData = { ...data, postback: postback }; /** @type {any[]} */ let files = []; - if (!!!data?.truncateMsgId) { + if (!!!messageData?.inputMessageId) { files = getChatFiles(); } resetStorage(); - return new Promise((resolve, reject) => { - sendMessageToHub(params.agentId, params.conversationId, msgText, messageData, files).then(res => { - isSendingMsg = false; - autoScrollLog = false; - resolve(res); - }).catch(err => { - isSendingMsg = false; - autoScrollLog = false; - reject(err); + if (files?.length > 0 && !!!messageData.inputMessageId) { + return new Promise((resolve, reject) => { + uploadConversationFiles(params.agentId, params.conversationId, files).then(resMessageId => { + messageData = { ...messageData, inputMessageId: resMessageId }; + sendMessageToHub(params.agentId, params.conversationId, msgText, messageData).then(res => { + resolve(res); + }).catch(err => { + reject(err); + }).finally(() => { + isSendingMsg = false; + autoScrollLog = false; + }); + }); }); - }); + } else { + return new Promise((resolve, reject) => { + sendMessageToHub(params.agentId, params.conversationId, msgText, messageData).then(res => { + resolve(res); + }).catch(err => { + reject(err); + }).finally(() => { + isSendingMsg = false; + autoScrollLog = false; + }); + }); + } } async function startListen() { @@ -560,34 +574,40 @@ } } - function toggleContentLog() { - isLoadContentLog = !isLoadContentLog; - if (!isLoadContentLog) { - contentLogs = []; - agentQueueLogs = []; - isContentLogClosed = true; - } else { - isContentLogClosed = false; + function openLogs() { + if (!isLoadPersistLog) { + isLoadPersistLog = true; + isPersistLogClosed = false; } + + if (!isLoadInstantLog) { + isLoadInstantLog = true; + isInstantLogClosed = false; + } + } + + function closePersistLog() { + isLoadPersistLog = false; + contentLogs = []; + convStateLogs = []; + isPersistLogClosed = true; } - function cleanContentLogScreen() { + function cleanPersistLogScreen() { contentLogs = []; + convStateLogs = []; } - function toggleStateLog() { - isLoadStateLog = !isLoadStateLog; - if (!isLoadStateLog) { - convStateLogs = []; - msgStateLogs = []; - isStateLogClosed = true; - } else { - isStateLogClosed = false; - } + function closeInstantLog() { + isLoadInstantLog = false; + msgStateLogs = []; + agentQueueLogs = []; + isInstantLogClosed = true; } - function cleanStateLogScreen() { - convStateLogs = []; + function cleanInstantLogScreen() { + msgStateLogs = []; + agentQueueLogs = []; } function toggleUserAddStateModal() { @@ -655,7 +675,9 @@ // @ts-ignore }).then(async (result) => { if (result.value) { - sendChatMessage(message?.text, { truncateMsgId: message?.message_id }); + deleteConversationMessage(params.conversationId, message?.message_id, true).then(resMessageId => { + sendChatMessage(message?.text, { inputMessageId: resMessageId }); + }); } }); } @@ -718,10 +740,12 @@ async function confirmEditMsg() { isOpenEditMsgModal = false; - sendChatMessage(editText, { truncateMsgId: truncateMsgId }).then(() => { - resetEditMsg(); - }).catch(() => { - resetEditMsg(); + deleteConversationMessage(params.conversationId, truncateMsgId, true).then(resMessageId => { + sendChatMessage(editText, { inputMessageId: resMessageId }).then(() => { + resetEditMsg(); + }).catch(() => { + resetEditMsg(); + }); }); } @@ -737,13 +761,11 @@ /** @param {string} messageId */ function truncateLogs(messageId) { - if (isLoadContentLog) { - const targetIdx = contentLogs.findIndex(x => x.message_id === messageId); + if (isLoadPersistLog) { + let targetIdx = contentLogs.findIndex(x => x.message_id === messageId); contentLogs = contentLogs.filter((x, idx) => idx < targetIdx); - } - - if (isLoadStateLog) { - const targetIdx = convStateLogs.findIndex(x => x.message_id === messageId); + + targetIdx = convStateLogs.findIndex(x => x.message_id === messageId); convStateLogs = convStateLogs.filter((x, idx) => idx < targetIdx); } } @@ -772,7 +794,7 @@ /** @param {string} messageId */ function highlightStateLog(messageId) { - if (!isLoadStateLog) return; + if (!isLoadInstantLog) return; const targets = document.querySelectorAll('.state-log-item'); targets.forEach(elm => { @@ -794,7 +816,7 @@ const stateLogWrapper = '.conv-state-log-scrollbar'; const elements = []; const contentLogElm = document.querySelector(`#content-log-${messageId}`); - if (isLoadContentLog && !!contentLogElm) { + if (isLoadPersistLog && !!contentLogElm) { elements.push({ elm: contentLogElm, wrapperName: contentLogWrapper @@ -802,7 +824,7 @@ } const stateLogElm = document.querySelector(`#state-log-${messageId}`); - if (isLoadStateLog && !!stateLogElm) { + if (isLoadPersistLog && !!stateLogElm) { elements.push({ elm: stateLogElm, wrapperName: stateLogWrapper @@ -841,9 +863,9 @@ title={'Edit message'} size={'md'} isOpen={isOpenEditMsgModal} - toggleModal={toggleEditMsgModal} - confirm={confirmEditMsg} - cancel={toggleEditMsgModal} + toggleModal={() => toggleEditMsgModal()} + confirm={() => confirmEditMsg()} + cancel={() => toggleEditMsgModal()} disableConfirmBtn={!!!_.trim(editText)} >