Skip to content

Commit 4fd4d07

Browse files
authored
Merge pull request #327 from iceljc/features/refine-chat-window
refine chat log
2 parents e0ca31b + 2a7866a commit 4fd4d07

File tree

10 files changed

+149
-43
lines changed

10 files changed

+149
-43
lines changed

src/lib/helpers/http.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ function skipLoader(config) {
108108
new RegExp('http(s*)://(.*?)/user/(.*?)/details', 'g'),
109109
new RegExp('http(s*)://(.*?)/agent/labels', 'g'),
110110
new RegExp('http(s*)://(.*?)/conversation/state/keys', 'g'),
111-
new RegExp('http(s*)://(.*?)/logger/instruction/log/keys', 'g')
111+
new RegExp('http(s*)://(.*?)/logger/instruction/log/keys', 'g'),
112+
new RegExp('http(s*)://(.*?)/logger/conversation/(.*?)/content-log', 'g'),
113+
new RegExp('http(s*)://(.*?)/logger/conversation/(.*?)/state-log', 'g')
112114
];
113115

114116
if (config.method === 'post' && postRegexes.some(regex => regex.test(config.url || ''))) {

src/lib/helpers/types/commonTypes.js

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@
2424
* @property {T[]} items - Items.
2525
*/
2626

27+
/**
28+
* @template T
29+
* @typedef {Object} DateTimePagedItems<T>
30+
* @property {number} count - Row count.
31+
* @property {T[]} items - Items.
32+
* @property {Date?} [nextTime]
33+
*/
34+
2735
/**
2836
* @typedef {Object} LlmConfigOption
2937
* @property {number?} [type]

src/lib/helpers/types/conversationTypes.js

+8
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ IRichContent.prototype.quick_replies;
152152

153153
/**
154154
* @typedef {Object} ConversationContentLogModel
155+
* @property {string?} [uid]
155156
* @property {string} conversation_id - The conversation id.
156157
* @property {string} message_id - The message id.
157158
* @property {string} name - The sender name.
@@ -164,12 +165,19 @@ IRichContent.prototype.quick_replies;
164165

165166
/**
166167
* @typedef {Object} ConversationStateLogModel
168+
* @property {string?} [uid]
167169
* @property {string} conversation_id - The conversation id.
168170
* @property {string} message_id - The message id.
169171
* @property {Object} states - The states content.
170172
* @property {Date} created_at - The log sent time.
171173
*/
172174

175+
/**
176+
* @typedef {Object} ConversationLogFilter
177+
* @property {number} size
178+
* @property {Date?} [startTime]
179+
*/
180+
173181
/**
174182
* @typedef {Object} MessageStateLogModel
175183
* @property {string} conversation_id - The conversation id.

src/lib/scss/custom/pages/_chat.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686

8787
.chat-head-info {
8888
display: flex;
89-
flex: 0 0 fit-content;
89+
// flex: 0 0 fit-content;
9090
flex-direction: column;
9191
height: 100%;
9292
gap: 5px;

src/lib/services/conversation-service.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,16 @@ export async function deleteConversation(conversationId) {
8080
/**
8181
* Get dialog history
8282
* @param {string} conversationId
83+
* @param {number} count
8384
* @returns {Promise<import('$conversationTypes').ChatResponseModel[]>}
8485
*/
85-
export async function getDialogs(conversationId) {
86+
export async function getDialogs(conversationId, count = 100) {
8687
let url = replaceUrl(endpoints.dialogsUrl, {conversationId: conversationId});
87-
const response = await axios.get(url);
88+
const response = await axios.get(url, {
89+
params: {
90+
count: count
91+
}
92+
});
8893
return response.data;
8994
}
9095

src/lib/services/logging-service.js

+18-8
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,32 @@ export async function getFullLog() {
2323

2424
/**
2525
* Get conversation content log
26-
* @param {string} conversationId
27-
* @returns {Promise<import('$conversationTypes').ConversationContentLogModel[]>}
26+
* @param {string} conversationId
27+
* @param {import('$conversationTypes').ConversationLogFilter} filter
28+
* @returns {Promise<import('$commonTypes').DateTimePagedItems<import('$conversationTypes').ConversationContentLogModel>>}
2829
*/
29-
export async function GetContentLogs(conversationId) {
30+
export async function getContentLogs(conversationId, filter) {
3031
let url = replaceUrl(endpoints.loggingContentLogUrl, {conversationId: conversationId});
31-
const response = await axios.get(url);
32+
const response = await axios.get(url, {
33+
params: {
34+
...filter
35+
}
36+
});
3237
return response.data;
3338
}
3439

3540
/**
3641
* Get conversation state log
37-
* @param {string} conversationId
38-
* @returns {Promise<import('$conversationTypes').ConversationStateLogModel[]>}
42+
* @param {string} conversationId
43+
* @param {import('$conversationTypes').ConversationLogFilter} filter
44+
* @returns {Promise<import('$commonTypes').DateTimePagedItems<import('$conversationTypes').ConversationStateLogModel>>}
3945
*/
40-
export async function GetStateLogs(conversationId) {
46+
export async function getStateLogs(conversationId, filter) {
4147
let url = replaceUrl(endpoints.loggingStateLogUrl, {conversationId: conversationId});
42-
const response = await axios.get(url);
48+
const response = await axios.get(url, {
49+
params: {
50+
...filter
51+
}
52+
});
4353
return response.data;
4454
}

src/routes/chat/[agentId]/[conversationId]/chat-box.svelte

+11-9
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,10 @@
8888
const params = $page.params;
8989
const messageLimit = 100;
9090
const screenWidthThreshold = 1024;
91-
const chatWidthThreshold = 300;
91+
const chatWidthThreshold = 500;
9292
const maxTextLength = 64000;
9393
const duration = 2000;
94+
const dialogCount = 50;
9495
const MESSAGE_STORAGE_KEY = 'message_draft_';
9596
9697
/** @type {import('$agentTypes').AgentModel} */
@@ -219,7 +220,7 @@
219220
onMount(async () => {
220221
disableSpeech = navigator.userAgent.includes('Firefox');
221222
conversation = await getConversation(params.conversationId);
222-
dialogs = await getDialogs(params.conversationId);
223+
dialogs = await getDialogs(params.conversationId, dialogCount);
223224
conversationUser = await getConversationUser(params.conversationId);
224225
selectedTags = conversation?.tags || [];
225226
initUserSentMessages(dialogs);
@@ -494,7 +495,8 @@
494495
/** @param {import('$conversationTypes').ConversationContentLogModel} log */
495496
function onConversationContentLogGenerated(log) {
496497
if (!isLoadPersistLog) return;
497-
contentLogs.push({ ...log });
498+
499+
contentLogs.push({ ...log, uid: uuidv4() });
498500
contentLogs = contentLogs.map(x => { return { ...x }; });
499501
}
500502
@@ -503,7 +505,7 @@
503505
if (!isLoadPersistLog) return;
504506
505507
latestStateLog = log;
506-
convStateLogs.push({ ...log });
508+
convStateLogs.push({ ...log, uid: uuidv4() });
507509
convStateLogs = convStateLogs.map(x => { return { ...x }; });
508510
}
509511
@@ -1489,7 +1491,7 @@
14891491
<div class="card mb-0" style="height: 100vh;">
14901492
<div class="border-bottom chat-head">
14911493
<div class="row chat-row">
1492-
<div class="col-md-4 col-7 chat-head-info">
1494+
<div class="col-md-4 col-4 chat-head-info">
14931495
<div class="chat-head-agent">
14941496
{#if agent?.icon_url}
14951497
<div class="line-align-center">
@@ -1507,9 +1509,9 @@
15071509
</div>
15081510
</div>
15091511
</div>
1510-
1511-
<div class="col-md-8 col-5">
1512-
<div class="user-chat-nav user-chat-nav-flex mb-0">
1512+
1513+
<div class="col-md-8 col-8">
1514+
<div class="user-chat-nav user-chat-nav-flex mb-0" style={`padding-top: ${!isFrame ? '5px' : '0px'};`}>
15131515
{#if PUBLIC_DEBUG_MODE === 'true' && isFrame}
15141516
<div class="">
15151517
<button
@@ -1913,7 +1915,7 @@
19131915
bind:contentLogs={contentLogs}
19141916
bind:convStateLogs={convStateLogs}
19151917
bind:lastestStateLog={latestStateLog}
1916-
autoScroll={autoScrollLog}
1918+
bind:autoScroll={autoScrollLog}
19171919
closeWindow={() => closePersistLog()}
19181920
cleanScreen={() => cleanPersistLogScreen()}
19191921
/>

src/routes/chat/[agentId]/[conversationId]/persist-log/content-log-element.svelte

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
];
2727
2828
$: {
29+
logDisplayStyle = '';
30+
logTextStyle = '';
2931
if (data.source === ContentLogSource.AgentResponse || data.source === ContentLogSource.Notification) {
3032
logDisplayStyle = 'border border-secondary';
3133
logTextStyle = 'text-info';
@@ -62,7 +64,7 @@
6264
{/if}
6365
</span>
6466
<span class="ms-2">{`${utcToLocal(data?.created_at, 'hh:mm:ss.SSS A, MMM DD YYYY')} `}</span>
65-
</div>
67+
</div>
6668
</div>
6769
<div
6870
class={`rounded log-content ${logDisplayStyle}`}

src/routes/chat/[agentId]/[conversationId]/persist-log/conversation-state-log-element.svelte

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
/>
1919
</div>
2020
{#if data.message_id}
21-
<div style="margin-top: 10px;">
22-
{`MessageId: ${data.message_id}`}
23-
</div>
21+
<div style="margin-top: 10px;">
22+
{`MessageId: ${data.message_id}`}
23+
</div>
2424
{/if}
2525
</div>

src/routes/chat/[agentId]/[conversationId]/persist-log/persist-log.svelte

+87-18
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
<script>
2+
import { afterUpdate, beforeUpdate, onDestroy, onMount, tick } from 'svelte';
3+
import { page } from '$app/stores';
4+
import moment from 'moment';
5+
import { v4 as uuidv4 } from 'uuid';
26
import 'overlayscrollbars/overlayscrollbars.css';
37
import { OverlayScrollbars } from 'overlayscrollbars';
4-
import { afterUpdate, beforeUpdate, onDestroy, onMount } from 'svelte';
5-
import { page } from '$app/stores';
6-
import { GetContentLogs, GetStateLogs } from '$lib/services/logging-service';
8+
import { getContentLogs, getStateLogs } from '$lib/services/logging-service';
79
import NavBar from '$lib/common/nav-bar/NavBar.svelte';
810
import NavItem from '$lib/common/nav-bar/NavItem.svelte';
911
import ContentLogElement from './content-log-element.svelte';
1012
import ConversationStateLogElement from './conversation-state-log-element.svelte';
11-
13+
1214
const contentLogTab = 1;
1315
const conversationStateLogTab = 2;
16+
const conversationId = $page.params.conversationId;
17+
const utcNow = moment.utc().toDate();
18+
19+
const scrollbarElements = [
20+
{
21+
id: '.content-log-scrollbar',
22+
type: contentLogTab,
23+
},
24+
{
25+
id: '.conv-state-log-scrollbar',
26+
type: conversationStateLogTab,
27+
}
28+
];
1429
1530
/** @type {import('$conversationTypes').ConversationContentLogModel[]} */
1631
export let contentLogs = [];
@@ -36,6 +51,11 @@
3651
let selectedTab = contentLogTab;
3752
let tabChanged = false;
3853
54+
/** @type {import('$conversationTypes').ConversationLogFilter} */
55+
let contentLogFilter = { size: 20, startTime: utcNow };
56+
/** @type {import('$conversationTypes').ConversationLogFilter} */
57+
let stateLogFilter = { size: 20, startTime: utcNow };
58+
3959
const options = {
4060
scrollbars: {
4161
visibility: 'auto',
@@ -49,18 +69,10 @@
4969
};
5070
5171
onMount(async () => {
52-
const conversationId = $page.params.conversationId;
53-
contentLogs = await GetContentLogs(conversationId);
54-
convStateLogs = await GetStateLogs(conversationId);
55-
lastestStateLog = convStateLogs.slice(-1)[0];
56-
57-
const scrollbarElements = [
58-
document.querySelector('.content-log-scrollbar'),
59-
document.querySelector('.conv-state-log-scrollbar')
60-
].filter(Boolean);
61-
scrollbarElements.forEach(elem => {
62-
scrollbars = [ ...scrollbars, OverlayScrollbars(elem, options) ];
63-
});
72+
await getChatContentLogs();
73+
await getChatStateLogs();
74+
75+
initScrollbars();
6476
scrollToBottom();
6577
});
6678
@@ -95,6 +107,63 @@
95107
}, 200);
96108
});
97109
}
110+
111+
function initScrollbars() {
112+
scrollbarElements.forEach(item => {
113+
const elem = document.querySelector(item.id);
114+
if (!elem) return;
115+
116+
const scrollbar = OverlayScrollbars(elem, options);
117+
scrollbar.on("scroll", async (e) => {
118+
const curScrollTop = e.elements().scrollOffsetElement.scrollTop;
119+
if (curScrollTop <= 1) {
120+
tabChanged = true;
121+
if (item.type === contentLogTab) {
122+
await getChatContentLogs();
123+
} else if (item.type === conversationStateLogTab) {
124+
await getChatStateLogs();
125+
}
126+
}
127+
});
128+
129+
scrollbars = [ ...scrollbars, scrollbar];
130+
});
131+
}
132+
133+
async function getChatContentLogs() {
134+
if (!contentLogFilter.startTime) return;
135+
136+
const pagedContentLogs = await getContentLogs(conversationId, contentLogFilter);
137+
contentLogFilter = {
138+
...contentLogFilter,
139+
startTime: pagedContentLogs.nextTime || null
140+
};
141+
const newLogs = pagedContentLogs.items?.map(x => {
142+
return { uid: uuidv4(), ...x };
143+
}) || [];
144+
145+
if (newLogs.length > 0) {
146+
contentLogs = [...newLogs, ...contentLogs];
147+
}
148+
}
149+
150+
async function getChatStateLogs() {
151+
if (!stateLogFilter.startTime) return;
152+
153+
const pagedStateLogs = await getStateLogs(conversationId, stateLogFilter);
154+
stateLogFilter = {
155+
...stateLogFilter,
156+
startTime: pagedStateLogs.nextTime || null
157+
};
158+
const newLogs = pagedStateLogs.items?.map(x => {
159+
return { uid: uuidv4(), ...x };
160+
}) || [];
161+
162+
if (newLogs.length > 0) {
163+
convStateLogs = [...newLogs, ...convStateLogs];
164+
lastestStateLog = convStateLogs.slice(-1)[0];
165+
}
166+
}
98167
99168
function cleanLogs() {
100169
contentLogs = [];
@@ -141,15 +210,15 @@
141210
142211
<div class="content-log-scrollbar log-list padding-side log-body" class:hide={selectedTab !== contentLogTab}>
143212
<ul>
144-
{#each contentLogs as log}
213+
{#each contentLogs as log (log.uid)}
145214
<ContentLogElement data={log} />
146215
{/each}
147216
</ul>
148217
</div>
149218
150219
<div class="conv-state-log-scrollbar log-list padding-side log-body" class:hide={selectedTab !== conversationStateLogTab}>
151220
<ul>
152-
{#each convStateLogs as log}
221+
{#each convStateLogs as log (log.uid)}
153222
<ConversationStateLogElement data={log} />
154223
{/each}
155224
</ul>

0 commit comments

Comments
 (0)