Improvements on the chat UI, prepare for supporting multiple models, and adding feedback to interactions.
This commit is contained in:
@@ -16,9 +16,10 @@ class EveAIChatWidget extends HTMLElement {
|
||||
this.innerHTML = this.getTemplate();
|
||||
this.messagesArea = this.querySelector('.messages-area');
|
||||
this.questionInput = this.querySelector('.question-area input');
|
||||
this.sendButton = this.querySelector('.send-icon');
|
||||
this.statusLine = this.querySelector('.status-line');
|
||||
|
||||
this.querySelector('.question-area button').addEventListener('click', () => this.handleSendMessage());
|
||||
this.sendButton.addEventListener('click', () => this.handleSendMessage());
|
||||
this.questionInput.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
this.handleSendMessage();
|
||||
@@ -34,12 +35,6 @@ class EveAIChatWidget extends HTMLElement {
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
console.log(`attributeChangedCallback called: ${name} changed from ${oldValue} to ${newValue}`);
|
||||
this.updateAttributes();
|
||||
console.log('Current attributes:', {
|
||||
tenantId: this.getAttribute('tenant-id'),
|
||||
apiKey: this.getAttribute('api-key'),
|
||||
domain: this.getAttribute('domain'),
|
||||
language: this.getAttribute('language')
|
||||
});
|
||||
|
||||
if (this.areAllAttributesSet() && !this.socket) {
|
||||
console.log('All attributes set in attributeChangedCallback, initializing socket');
|
||||
@@ -49,7 +44,6 @@ class EveAIChatWidget extends HTMLElement {
|
||||
}
|
||||
|
||||
updateAttributes() {
|
||||
console.log('Updating attributes:');
|
||||
this.tenantId = this.getAttribute('tenant-id');
|
||||
this.apiKey = this.getAttribute('api-key');
|
||||
this.domain = this.getAttribute('domain');
|
||||
@@ -73,7 +67,7 @@ class EveAIChatWidget extends HTMLElement {
|
||||
domain,
|
||||
language
|
||||
});
|
||||
return tenantId && apiKey && domain;
|
||||
return tenantId && apiKey && domain && language;
|
||||
}
|
||||
|
||||
initializeSocket() {
|
||||
@@ -83,7 +77,7 @@ class EveAIChatWidget extends HTMLElement {
|
||||
}
|
||||
if (!this.domain || this.domain === 'null') {
|
||||
console.error('Domain attribute is missing or invalid');
|
||||
this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.')
|
||||
this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.');
|
||||
return;
|
||||
}
|
||||
console.log(`Initializing socket connection to ${this.domain}`);
|
||||
@@ -101,14 +95,16 @@ class EveAIChatWidget extends HTMLElement {
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Finished initializing socket connection to ${this.domain}`);
|
||||
|
||||
this.socket.on('connect', (data) => {
|
||||
console.log('Socket connected');
|
||||
this.setStatusMessage('Connected to EveAI.')
|
||||
console.log('Socket connected OK');
|
||||
this.setStatusMessage('Connected to EveAI.');
|
||||
});
|
||||
|
||||
this.socket.on('authenticated', (data) => {
|
||||
console.log('Authenticated event received: ', data);
|
||||
this.setStatusMessage('Authenticated.')
|
||||
this.setStatusMessage('Authenticated.');
|
||||
if (data.token) {
|
||||
this.jwtToken = data.token; // Store the JWT token received from the server
|
||||
}
|
||||
@@ -116,31 +112,31 @@ class EveAIChatWidget extends HTMLElement {
|
||||
|
||||
this.socket.on('connect_error', (err) => {
|
||||
console.error('Socket connection error:', err);
|
||||
this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.')
|
||||
this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.');
|
||||
});
|
||||
|
||||
this.socket.on('connect_timeout', () => {
|
||||
console.error('Socket connection timeout');
|
||||
this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.')
|
||||
this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.');
|
||||
});
|
||||
|
||||
this.socket.on('disconnect', () => {
|
||||
console.log('Socket disconnected');
|
||||
this.setStatusMessage('Disconnected from EveAI. Please refresh the page for further interaction.')
|
||||
this.setStatusMessage('Disconnected from EveAI. Please refresh the page for further interaction.');
|
||||
});
|
||||
|
||||
this.socket.on('bot_response', (data) => {
|
||||
if (data.tenantId === this.tenantId) {
|
||||
console.log('Initial response received:', data)
|
||||
console.log('Task ID received:', data.taskId)
|
||||
this.checkTaskStatus(data.taskId)
|
||||
this.setStatusMessage('Processing...')
|
||||
console.log('Initial response received:', data);
|
||||
console.log('Task ID received:', data.taskId);
|
||||
this.checkTaskStatus(data.taskId);
|
||||
this.setStatusMessage('Processing...');
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('task_status', (data) => {
|
||||
console.log('Task status received:', data.status)
|
||||
console.log('Task ID received:', data.taskId)
|
||||
console.log('Task status received:', data.status);
|
||||
console.log('Task ID received:', data.taskId);
|
||||
console.log('Citations type:', typeof data.citations, 'Citations:', data.citations);
|
||||
|
||||
if (data.status === 'pending') {
|
||||
@@ -169,6 +165,7 @@ class EveAIChatWidget extends HTMLElement {
|
||||
|
||||
clearProgress() {
|
||||
this.statusLine.textContent = '';
|
||||
this.toggleSendButton(false); // Re-enable and revert send button to outlined version
|
||||
}
|
||||
|
||||
checkTaskStatus(taskId) {
|
||||
@@ -182,7 +179,7 @@ class EveAIChatWidget extends HTMLElement {
|
||||
<div class="messages-area"></div>
|
||||
<div class="question-area">
|
||||
<input type="text" placeholder="Type your message here..." />
|
||||
<button>Send</button>
|
||||
<i class="material-icons send-icon outlined">send</i>
|
||||
</div>
|
||||
<div class="status-line"></div>
|
||||
</div>
|
||||
@@ -197,31 +194,89 @@ class EveAIChatWidget extends HTMLElement {
|
||||
this.messagesArea.scrollTop = this.messagesArea.scrollHeight;
|
||||
}
|
||||
|
||||
handleFeedback(feedback, interactionId) {
|
||||
// Send feedback to the backend
|
||||
console.log('handleFeedback called');
|
||||
if (!this.socket) {
|
||||
console.error('Socket is not initialized');
|
||||
return;
|
||||
}
|
||||
if (!this.jwtToken) {
|
||||
console.error('JWT token is not available');
|
||||
return;
|
||||
}
|
||||
console.log('Sending message to backend');
|
||||
console.log(`Feedback for ${interactionId}: ${feedback}`);
|
||||
this.socket.emit('feedback', { tenantId: this.tenantId, token: this.jwtToken, feedback, interactionId });
|
||||
this.setStatusMessage('Feedback sent.');
|
||||
}
|
||||
|
||||
addBotMessage(text, interactionId, algorithm = 'default', citations = []) {
|
||||
const message = document.createElement('div');
|
||||
message.classList.add('message', 'bot');
|
||||
|
||||
let content = marked.parse(text); // Use marked to convert markdown to HTML
|
||||
// Ensure citations is an array
|
||||
if (!Array.isArray(citations)) {
|
||||
console.error('Expected citations to be an array, but got:', citations);
|
||||
citations = []; // Default to an empty array
|
||||
}
|
||||
let content = marked.parse(text);
|
||||
let citationsHtml = citations.map(url => `<a href="${url}" target="_blank">${url}</a>`).join('<br>');
|
||||
|
||||
message.innerHTML = `
|
||||
<p>${content}</p>
|
||||
${citationsHtml ? `<p>${citationsHtml}</p>` : ''}
|
||||
<div class="message-icons">
|
||||
<span class="algorithm-indicator" style="background-color: var(--algorithm-color-${algorithm});">${algorithm}</span>
|
||||
<i class="material-icons" onclick="handleFeedback('up', '${interactionId}')">thumb_up</i>
|
||||
<i class="material-icons" onclick="handleFeedback('down', '${interactionId}')">thumb_down</i>
|
||||
</div>
|
||||
`;
|
||||
this.messagesArea.appendChild(message);
|
||||
let algorithmClass;
|
||||
switch (algorithm) {
|
||||
case 'RAG_TENANT':
|
||||
algorithmClass = 'fingerprint-rag-tenant';
|
||||
break;
|
||||
case 'RAG_WIKIPEDIA':
|
||||
algorithmClass = 'fingerprint-rag-wikipedia';
|
||||
break;
|
||||
case 'RAG_GOOGLE':
|
||||
algorithmClass = 'fingerprint-rag-google';
|
||||
break;
|
||||
case 'RAG_LLM':
|
||||
algorithmClass = 'fingerprint-rag-llm';
|
||||
break;
|
||||
default:
|
||||
algorithmClass = '';
|
||||
}
|
||||
|
||||
message.innerHTML = `
|
||||
<p>${content}</p>
|
||||
${citationsHtml ? `<p class="citations">${citationsHtml}</p>` : ''}
|
||||
<div class="message-icons">
|
||||
<i class="material-icons ${algorithmClass}">fingerprint</i>
|
||||
<i class="material-icons thumb-icon outlined" data-feedback="up" data-interaction-id="${interactionId}">thumb_up_off_alt</i>
|
||||
<i class="material-icons thumb-icon outlined" data-feedback="down" data-interaction-id="${interactionId}">thumb_down_off_alt</i>
|
||||
</div>
|
||||
`;
|
||||
this.messagesArea.appendChild(message);
|
||||
|
||||
// Add event listeners for feedback buttons
|
||||
const thumbsUp = message.querySelector('i[data-feedback="up"]');
|
||||
const thumbsDown = message.querySelector('i[data-feedback="down"]');
|
||||
thumbsUp.addEventListener('click', () => this.toggleFeedback(thumbsUp, thumbsDown, 'up', interactionId));
|
||||
thumbsDown.addEventListener('click', () => this.toggleFeedback(thumbsUp, thumbsDown, 'down', interactionId));
|
||||
|
||||
this.messagesArea.scrollTop = this.messagesArea.scrollHeight;
|
||||
}
|
||||
|
||||
toggleFeedback(thumbsUp, thumbsDown, feedback, interactionId) {
|
||||
if (feedback === 'up') {
|
||||
thumbsUp.textContent = 'thumb_up'; // Change to filled icon
|
||||
thumbsUp.classList.remove('outlined');
|
||||
thumbsUp.classList.add('filled');
|
||||
thumbsDown.textContent = 'thumb_down_off_alt'; // Keep the other icon outlined
|
||||
thumbsDown.classList.add('outlined');
|
||||
thumbsDown.classList.remove('filled');
|
||||
} else {
|
||||
thumbsDown.textContent = 'thumb_down'; // Change to filled icon
|
||||
thumbsDown.classList.remove('outlined');
|
||||
thumbsDown.classList.add('filled');
|
||||
thumbsUp.textContent = 'thumb_up_off_alt'; // Keep the other icon outlined
|
||||
thumbsUp.classList.add('outlined');
|
||||
thumbsUp.classList.remove('filled');
|
||||
}
|
||||
|
||||
// Send feedback to the backend
|
||||
this.handleFeedback(feedback, interactionId);
|
||||
}
|
||||
|
||||
handleSendMessage() {
|
||||
console.log('handleSendMessage called');
|
||||
const message = this.questionInput.value.trim();
|
||||
@@ -229,6 +284,7 @@ class EveAIChatWidget extends HTMLElement {
|
||||
this.addUserMessage(message);
|
||||
this.questionInput.value = '';
|
||||
this.sendMessageToBackend(message);
|
||||
this.toggleSendButton(true); // Disable and change send button to filled version
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,12 +302,23 @@ class EveAIChatWidget extends HTMLElement {
|
||||
this.socket.emit('user_message', { tenantId: this.tenantId, token: this.jwtToken, message, language: this.language });
|
||||
this.setStatusMessage('Processing started ...')
|
||||
}
|
||||
|
||||
toggleSendButton(isProcessing) {
|
||||
if (isProcessing) {
|
||||
this.sendButton.textContent = 'send'; // Filled send icon
|
||||
this.sendButton.classList.remove('outlined');
|
||||
this.sendButton.classList.add('filled');
|
||||
this.sendButton.classList.add('disabled'); // Add disabled class for styling
|
||||
this.sendButton.style.pointerEvents = 'none'; // Disable click events
|
||||
} else {
|
||||
this.sendButton.textContent = 'send'; // Outlined send icon
|
||||
this.sendButton.classList.add('outlined');
|
||||
this.sendButton.classList.remove('filled');
|
||||
this.sendButton.classList.remove('disabled'); // Remove disabled class
|
||||
this.sendButton.style.pointerEvents = 'auto'; // Re-enable click events
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('eveai-chat-widget', EveAIChatWidget);
|
||||
|
||||
function handleFeedback(feedback, interactionId) {
|
||||
// Send feedback to the backend
|
||||
console.log(`Feedback for ${interactionId}: ${feedback}`);
|
||||
this.socket.emit('feedback', { feedback, interaction_id: interactionId });
|
||||
}
|
||||
|
||||
@@ -19,12 +19,6 @@ class EveAI {
|
||||
chatWidget.setAttribute('api-key', this.apiKey);
|
||||
chatWidget.setAttribute('domain', this.domain);
|
||||
chatWidget.setAttribute('language', this.language);
|
||||
console.log('Attributes set in chat widget:', {
|
||||
tenantId: chatWidget.getAttribute('tenant-id'),
|
||||
apiKey: chatWidget.getAttribute('api-key'),
|
||||
domain: chatWidget.getAttribute('domain'),
|
||||
language: chatWidget.getAttribute('language')
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error('Container not found');
|
||||
|
||||
Reference in New Issue
Block a user