From bc350af24748e8bcdbe61bda50d3c3859e3a72ed Mon Sep 17 00:00:00 2001 From: Josako Date: Wed, 28 Aug 2024 10:11:31 +0200 Subject: [PATCH] - Allow the chat-widget to connect to multiple servers (e.g. development and production) - Created a full session overview --- docker/compose_dev.yaml | 3 + docker/nginx/Dockerfile | 3 + .../interaction/view_chat_session.html | 154 ++---- eveai_app/views/interaction_views.py | 30 +- integrations/Wordpress/eveai-chat-widget.zip | Bin 13805 -> 13942 bytes .../eveai-chat-widget/eveai-chat_plugin.php | 9 +- .../eveai-chat-widget/js/eveai-chat-widget.js | 14 +- .../eveai-chat-widget/js/eveai-sdk.js | 6 +- .../Wordpress/eveai-chat-widget/readme.txt | 9 +- nginx/public/chat_evie.html | 30 ++ nginx/static/assets/css/eveai.css | 87 ++++ nginx/static/css/eveai-chat-style.css | 294 ----------- nginx/static/js/eveai-chat-widget.js | 470 ------------------ nginx/static/js/eveai-sdk.js | 29 -- 14 files changed, 229 insertions(+), 909 deletions(-) create mode 100644 nginx/public/chat_evie.html delete mode 100644 nginx/static/css/eveai-chat-style.css delete mode 100644 nginx/static/js/eveai-chat-widget.js delete mode 100644 nginx/static/js/eveai-sdk.js diff --git a/docker/compose_dev.yaml b/docker/compose_dev.yaml index e2ef793..683b497 100644 --- a/docker/compose_dev.yaml +++ b/docker/compose_dev.yaml @@ -57,6 +57,9 @@ services: - ../nginx/sites-enabled:/etc/nginx/sites-enabled - ../nginx/static:/etc/nginx/static - ../nginx/public:/etc/nginx/public + - ../integrations/Wordpress/eveai-chat-widget/css/eveai-chat-style.css:/etc/nginx/static/css/eveai-chat-style.css + - ../integrations/Wordpress/eveai-chat-widget/js/eveai-chat-widget.js:/etc/nginx/static/js/eveai-chat-widget.js + - ../integrations/Wordpress/eveai-chat-widget/js/eveai-sdk.js:/etc/nginx/static/js/eveai-sdk.js - ./logs/nginx:/var/log/nginx depends_on: - eveai_app diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile index 7328492..608e80d 100644 --- a/docker/nginx/Dockerfile +++ b/docker/nginx/Dockerfile @@ -10,6 +10,9 @@ COPY ../../nginx/mime.types /etc/nginx/mime.types # Copy static & public files RUN mkdir -p /etc/nginx/static /etc/nginx/public COPY ../../nginx/static /etc/nginx/static +COPY ../../integrations/Wordpress/eveai-chat-widget/css/eveai-chat-style.css /etc/nginx/static/css/ +COPY ../../integrations/Wordpress/eveai-chat-widget/js/eveai-chat-widget.js /etc/nginx/static/js/ +COPY ../../integrations/Wordpress/eveai-chat-widget/js/eveai-sdk.js /etc/nginx/static/js COPY ../../nginx/public /etc/nginx/public # Copy site-specific configurations diff --git a/eveai_app/templates/interaction/view_chat_session.html b/eveai_app/templates/interaction/view_chat_session.html index 48d0c34..ef80a60 100644 --- a/eveai_app/templates/interaction/view_chat_session.html +++ b/eveai_app/templates/interaction/view_chat_session.html @@ -1,126 +1,80 @@ {% extends "base.html" %} +{% from "macros.html" import render_field %} + +{% block title %}Session Overview{% endblock %} + +{% block content_title %}Session Overview{% endblock %} +{% block content_description %}An overview of the chat session.{% endblock %} {% block content %}

Chat Session Details

-
Session Information
- -
- - -
-
-
Session ID:
-
{{ chat_session.session_id }}
- -
Session Start:
-
- {{ chat_session.session_start | to_local_time(chat_session.timezone) }} - {{ chat_session.session_start | to_local_time(session['admin_user_timezone']) }} -
- -
Session End:
-
- {% if chat_session.session_end %} - {{ chat_session.session_end | to_local_time(chat_session.timezone) }} - {{ chat_session.session_end | to_local_time(session['admin_user_timezone']) }} - {% else %} - Ongoing - {% endif %} -
-
+

Session ID: {{ chat_session.session_id }}

+

User: {{ chat_session.user.user_name if chat_session.user else 'Anonymous' }}

+

Start: {{ chat_session.session_start | to_local_time(chat_session.timezone) }}

+

End: {{ chat_session.session_end | to_local_time(chat_session.timezone) if chat_session.session_end else 'Ongoing' }}

- -
-
-
Interactions
-
-
- {% for interaction in interactions %} -
-
-
- Question: - - {{ interaction.question_at | to_local_time(interaction.timezone) }} - {{ interaction.question_at | to_local_time(session['admin_user_timezone']) }} - - - {{ interaction.answer_at | to_local_time(interaction.timezone) }} - {{ interaction.answer_at | to_local_time(session['admin_user_timezone']) }} - ({{ interaction.question_at | time_difference(interaction.answer_at) }}) - -
-
-

Question: {{ interaction.question }}

-

Answer: {{ interaction.answer }}

-

- Algorithm Used: - - fingerprint - {{ interaction.algorithm_used }} -

-

- Appreciation: - - {{ 'thumb_up' if interaction.appreciation == 1 else 'thumb_down' }} - -

-

Embeddings: - {% if interaction.embeddings %} - {% for embedding in interaction.embeddings %} - - {{ embedding.embedding_id }} - - {% endfor %} - {% else %} - None - {% endif %} -

-
+

Interactions

+
+ {% for interaction in interactions %} +
+

+ +

+
+
+
Detailed Question:
+

{{ interaction.detailed_question }}

+
Answer:
+
{{ interaction.answer | safe }}
+ {% if embeddings_dict.get(interaction.id) %} +
Related Documents:
+
    + {% for embedding in embeddings_dict[interaction.id] %} +
  • + {% if embedding.url %} + {{ embedding.url }} + {% else %} + {{ embedding.file_name }} + {% endif %} +
  • + {% endfor %} +
+ {% endif %}
- {% endfor %} +
+ {% endfor %}
{% endblock %} {% block scripts %} + diff --git a/eveai_app/views/interaction_views.py b/eveai_app/views/interaction_views.py index 7d7a807..a866ca9 100644 --- a/eveai_app/views/interaction_views.py +++ b/eveai_app/views/interaction_views.py @@ -15,7 +15,8 @@ from requests.exceptions import SSLError from urllib.parse import urlparse import io -from common.models.interaction import ChatSession, Interaction +from common.models.document import Embedding, DocumentVersion +from common.models.interaction import ChatSession, Interaction, InteractionEmbedding from common.extensions import db from .document_forms import AddDocumentForm, AddURLForm, EditDocumentForm, EditDocumentVersionForm from common.utils.middleware import mw_before_request @@ -80,11 +81,34 @@ def handle_chat_session_selection(): return redirect(prefixed_url_for('interaction_bp.chat_sessions')) -@interaction_bp.route('/view_chat_session/', methods=['GET']) +@interaction_bp.route('/view_chat_session/', methods=['GET']) @roles_accepted('Super User', 'Tenant Admin') def view_chat_session(chat_session_id): chat_session = ChatSession.query.get_or_404(chat_session_id) - show_chat_session(chat_session) + interactions = (Interaction.query + .filter_by(chat_session_id=chat_session.id) + .order_by(Interaction.question_at) + .all()) + + # Fetch all related embeddings for the interactions in this session + embedding_query = (db.session.query(InteractionEmbedding.interaction_id, + DocumentVersion.url, + DocumentVersion.file_name) + .join(Embedding, InteractionEmbedding.embedding_id == Embedding.id) + .join(DocumentVersion, Embedding.doc_vers_id == DocumentVersion.id) + .filter(InteractionEmbedding.interaction_id.in_([i.id for i in interactions]))) + + # Create a dictionary to store embeddings for each interaction + embeddings_dict = {} + for interaction_id, url, file_name in embedding_query: + if interaction_id not in embeddings_dict: + embeddings_dict[interaction_id] = [] + embeddings_dict[interaction_id].append({'url': url, 'file_name': file_name}) + + return render_template('interaction/view_chat_session.html', + chat_session=chat_session, + interactions=interactions, + embeddings_dict=embeddings_dict) @interaction_bp.route('/view_chat_session_by_session_id/', methods=['GET']) diff --git a/integrations/Wordpress/eveai-chat-widget.zip b/integrations/Wordpress/eveai-chat-widget.zip index e65d7851db8ce35cb9266f82c0025465b64ece4e..0be574776574578c1a484edd62211c802829933d 100644 GIT binary patch delta 8304 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 U@cX1sTY>sH$>7YGo|EMN06h~c`Tzg` delta 8032 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 W13Jz2-*K?7d%;hQyvzo}{C@xwAh;O- diff --git a/integrations/Wordpress/eveai-chat-widget/eveai-chat_plugin.php b/integrations/Wordpress/eveai-chat-widget/eveai-chat_plugin.php index f0136b0..7b918aa 100644 --- a/integrations/Wordpress/eveai-chat-widget/eveai-chat_plugin.php +++ b/integrations/Wordpress/eveai-chat-widget/eveai-chat_plugin.php @@ -3,7 +3,7 @@ Plugin Name: EveAI Chat Widget Plugin URI: https://askeveai.com/ Description: Integrates the EveAI chat interface into your WordPress site. -Version: 1.4.1 +Version: 1.5.0 Author: Josako, Pieter Laroy Author URI: https://askeveai.com/about/ */ @@ -28,7 +28,8 @@ function eveai_chat_shortcode($atts) { 'api_key' => '', 'domain' => '', 'language' => 'en', - 'supported_languages' => 'en,fr,de,es' + 'supported_languages' => 'en,fr,de,es', + 'server_url' => 'https://evie.askeveai.com' ); // Merge provided attributes with defaults @@ -40,6 +41,7 @@ function eveai_chat_shortcode($atts) { $domain = esc_url_raw($atts['domain']); $language = sanitize_text_field($atts['language']); $supported_languages = sanitize_text_field($atts['supported_languages']); + $server_url = esc_url_raw($atts['server_url']); // Generate a unique ID for this instance of the chat widget $chat_id = 'chat-container-' . uniqid(); @@ -52,7 +54,8 @@ function eveai_chat_shortcode($atts) { '$api_key', '$domain', '$language', - '$supported_languages' + '$supported_languages', + '$server_url' ); eveAI.initializeChat('$chat_id'); }); diff --git a/integrations/Wordpress/eveai-chat-widget/js/eveai-chat-widget.js b/integrations/Wordpress/eveai-chat-widget/js/eveai-chat-widget.js index b1b1bae..3dc82e8 100644 --- a/integrations/Wordpress/eveai-chat-widget/js/eveai-chat-widget.js +++ b/integrations/Wordpress/eveai-chat-widget/js/eveai-chat-widget.js @@ -1,6 +1,6 @@ class EveAIChatWidget extends HTMLElement { static get observedAttributes() { - return ['tenant-id', 'api-key', 'domain', 'language', 'languages']; + return ['tenant-id', 'api-key', 'domain', 'language', 'languages', 'server-url']; } constructor() { @@ -87,6 +87,7 @@ class EveAIChatWidget extends HTMLElement { this.language = this.getAttribute('language'); const languageAttr = this.getAttribute('languages'); this.languages = languageAttr ? languageAttr.split(',') : []; + this.serverUrl = this.getAttribute('server-url'); this.currentLanguage = this.language; console.log('Updated attributes:', { tenantId: this.tenantId, @@ -94,7 +95,8 @@ class EveAIChatWidget extends HTMLElement { domain: this.domain, language: this.language, currentLanguage: this.currentLanguage, - languages: this.languages + languages: this.languages, + serverUrl: this.serverUrl }); } @@ -104,14 +106,16 @@ class EveAIChatWidget extends HTMLElement { const domain = this.getAttribute('domain'); const language = this.getAttribute('language'); const languages = this.getAttribute('languages'); + const serverUrl = this.getAttribute('server-url'); console.log('Checking if all attributes are set:', { tenantId, apiKey, domain, language, - languages + languages, + serverUrl }); - return tenantId && apiKey && domain && language && languages; + return tenantId && apiKey && domain && language && languages && serverUrl; } createLanguageDropdown() { @@ -142,7 +146,7 @@ class EveAIChatWidget extends HTMLElement { console.log(`Initializing socket connection to Evie`); // Ensure apiKey is passed in the query parameters - this.socket = io('https://evie.askeveai.com', { + this.socket = io(this.serverUrl, { path: '/chat/socket.io/', transports: ['websocket', 'polling'], query: { diff --git a/integrations/Wordpress/eveai-chat-widget/js/eveai-sdk.js b/integrations/Wordpress/eveai-chat-widget/js/eveai-sdk.js index 1e9ccb8..c43707b 100644 --- a/integrations/Wordpress/eveai-chat-widget/js/eveai-sdk.js +++ b/integrations/Wordpress/eveai-chat-widget/js/eveai-sdk.js @@ -1,13 +1,14 @@ // static/js/eveai-sdk.js class EveAI { - constructor(tenantId, apiKey, domain, language, languages) { + constructor(tenantId, apiKey, domain, language, languages, serverUrl) { this.tenantId = tenantId; this.apiKey = apiKey; this.domain = domain; this.language = language; this.languages = languages; + this.serverUrl = serverUrl; - console.log('EveAI constructor:', { tenantId, apiKey, domain, language, languages }); + console.log('EveAI constructor:', { tenantId, apiKey, domain, language, languages, serverUrl }); } initializeChat(containerId) { @@ -21,6 +22,7 @@ class EveAI { chatWidget.setAttribute('domain', this.domain); chatWidget.setAttribute('language', this.language); chatWidget.setAttribute('languages', this.languages); + chatWidget.setAttribute('server-url', this.serverUrl); }); } else { console.error('Container not found'); diff --git a/integrations/Wordpress/eveai-chat-widget/readme.txt b/integrations/Wordpress/eveai-chat-widget/readme.txt index 087696a..884ec2b 100644 --- a/integrations/Wordpress/eveai-chat-widget/readme.txt +++ b/integrations/Wordpress/eveai-chat-widget/readme.txt @@ -3,7 +3,7 @@ Contributors: Josako Tags: chat, ai Requires at least: 5.0 Tested up to: 5.9 -Stable tag: 1.4.1 +Stable tag: 1.5.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -23,10 +23,10 @@ This plugin allows you to easily add the EveAI chat widget to your WordPress sit To add an EveAI Chat Widget to your page or post, use the following shortcode: -[eveai_chat tenant_id="YOUR_TENANT_ID" api_key="YOUR_API_KEY" domain="YOUR_DOMAIN" language="LANGUAGE_CODE" supported_languages="COMMA_SEPARATED_LANGUAGE_CODES"] +[eveai_chat tenant_id="YOUR_TENANT_ID" api_key="YOUR_API_KEY" domain="YOUR_DOMAIN" language="LANGUAGE_CODE" supported_languages="COMMA_SEPARATED_LANGUAGE_CODES" server_url="Server URL for Evie"] Example: -[eveai_chat tenant_id="123456" api_key="your_api_key_here" domain="https://your-domain.com" language="en" supported_languages="en,fr,de,es"] +[eveai_chat tenant_id="123456" api_key="your_api_key_here" domain="https://your-domain.com" language="en" supported_languages="en,fr,de,es" server_url="https://evie.askeveai.com"] You can add multiple chat widgets with different configurations by using the shortcode multiple times with different parameters. @@ -38,6 +38,9 @@ Contact your EveAI service provider to obtain your Tenant ID, API Key, and Domai == Changelog == += 1.5.0 = +* Allow for multiple servers to serve Evie + = 1.4.1 - 1.4...= * Bug fixes diff --git a/nginx/public/chat_evie.html b/nginx/public/chat_evie.html new file mode 100644 index 0000000..9d957f7 --- /dev/null +++ b/nginx/public/chat_evie.html @@ -0,0 +1,30 @@ + + + + + + Chat Client Evie + + + + + + + + +
+ + + \ No newline at end of file diff --git a/nginx/static/assets/css/eveai.css b/nginx/static/assets/css/eveai.css index ee97655..2310b69 100644 --- a/nginx/static/assets/css/eveai.css +++ b/nginx/static/assets/css/eveai.css @@ -421,3 +421,90 @@ input[type="radio"] { box-shadow: none; } +/* Custom styles for chat session view */ +.accordion-button:not(.collapsed) { + background-color: var(--bs-primary); + color: var(--bs-white); +} + +.accordion-button:focus { + box-shadow: 0 0 0 0.25rem rgba(118, 89, 154, 0.25); +} + +.interaction-question { + font-size: 1rem; /* Normal text size */ +} + +.interaction-icons { + display: flex; + align-items: center; +} + +.interaction-icons .material-icons { + font-size: 24px; + margin-left: 8px; +} + +.thumb-icon.filled { + color: var(--bs-success); +} + +.thumb-icon.outlined { + color: var(--thumb-icon-outlined); +} + +/* Algorithm icon colors */ +.algorithm-icon.rag_tenant { + color: var(--algorithm-color-rag-tenant); +} + +.algorithm-icon.rag_wikipedia { + color: var(--algorithm-color-rag-wikipedia); +} + +.algorithm-icon.rag_google { + color: var(--algorithm-color-rag-google); +} + +.algorithm-icon.llm { + color: var(--algorithm-color-llm); +} + +.accordion-body { + background-color: var(--bs-light); +} + +/* Markdown content styles */ +.markdown-content { + font-size: 1rem; + line-height: 1.5; +} + +.markdown-content p { + margin-bottom: 1rem; +} + +.markdown-content h1, .markdown-content h2, .markdown-content h3, +.markdown-content h4, .markdown-content h5, .markdown-content h6 { + margin-top: 1.5rem; + margin-bottom: 1rem; +} + +.markdown-content ul, .markdown-content ol { + margin-bottom: 1rem; + padding-left: 2rem; +} + +.markdown-content code { + background-color: #f8f9fa; + padding: 0.2em 0.4em; + border-radius: 3px; +} + +.markdown-content pre { + background-color: #f8f9fa; + padding: 1rem; + border-radius: 5px; + overflow-x: auto; +} + diff --git a/nginx/static/css/eveai-chat-style.css b/nginx/static/css/eveai-chat-style.css deleted file mode 100644 index 1105f39..0000000 --- a/nginx/static/css/eveai-chat-style.css +++ /dev/null @@ -1,294 +0,0 @@ -/* eveai_chat.css */ -:root { - --user-message-bg: #292929; /* Default user message background color */ - --bot-message-bg: #1e1e1e; /* Default bot message background color */ - --chat-bg: #1e1e1e; /* Default chat background color */ - --status-line-color: #e9e9e9; /* Color for the status line text */ - --status-line-bg: #1e1e1e; /* Background color for the status line */ - --status-line-height: 30px; /* Fixed height for the status line */ - - --algorithm-color-rag-tenant: #0f0; /* Green for RAG_TENANT */ - --algorithm-color-rag-wikipedia: #00f; /* Blue for RAG_WIKIPEDIA */ - --algorithm-color-rag-google: #ff0; /* Yellow for RAG_GOOGLE */ - --algorithm-color-llm: #800080; /* Purple for RAG_LLM */ - - /*--font-family: 'Arial, sans-serif'; !* Default font family *!*/ - --font-family: 'Segoe UI, Roboto, Cantarell, Noto Sans, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol'; - --font-color: #e9e9e9; /* Default font color */ - --user-message-font-color: #e9e9e9; /* User message font color */ - --bot-message-font-color: #e9e9e9; /* Bot message font color */ - --input-bg: #292929; /* Input background color */ - --input-border: #ccc; /* Input border color */ - --input-text-color: #e9e9e9; /* Input text color */ - --button-color: #007bff; /* Button text color */ - - /* Variables for hyperlink backgrounds */ - --link-bg: #1e1e1e; /* Default background color for hyperlinks */ - --link-hover-bg: #1e1e1e; /* Background color on hover for hyperlinks */ - --link-color: #dec981; /* Default text color for hyperlinks */ - --link-hover-color: #D68F53; /* Text color on hover for hyperlinks */ - - /* New scrollbar variables */ - --scrollbar-bg: #292929; /* Background color for the scrollbar track */ - --scrollbar-thumb: #4b4b4b; /* Color for the scrollbar thumb */ - --scrollbar-thumb-hover: #dec981; /* Color for the thumb on hover */ - --scrollbar-thumb-active: #D68F53; /* Color for the thumb when active (dragged) */ - - /* Thumb colors */ - --thumb-icon-outlined: #4b4b4b; - --thumb-icon-filled: #e9e9e9; - - /* Connection Status colors */ - --status-connected-color: #28a745; /* Green color for connected status */ - --status-disconnected-color: #ffc107; /* Orange color for disconnected status */ -} - -/* Connection status styles */ -.connection-status-icon { - vertical-align: middle; - font-size: 24px; - margin-right: 8px; -} - -.status-connected { - color: var(--status-connected-color); -} - -.status-disconnected { - color: var(--status-disconnected-color); -} - -/* Custom scrollbar styles */ -.messages-area::-webkit-scrollbar { - width: 12px; /* Width of the scrollbar */ -} - -.messages-area::-webkit-scrollbar-track { - background: var(--scrollbar-bg); /* Background color for the track */ -} - -.messages-area::-webkit-scrollbar-thumb { - background-color: var(--scrollbar-thumb); /* Color of the thumb */ - border-radius: 10px; /* Rounded corners for the thumb */ - border: 3px solid var(--scrollbar-bg); /* Space around the thumb */ -} - -.messages-area::-webkit-scrollbar-thumb:hover { - background-color: var(--scrollbar-thumb-hover); /* Color when hovering over the thumb */ -} - -.messages-area::-webkit-scrollbar-thumb:active { - background-color: var(--scrollbar-thumb-active); /* Color when active (dragging) */ -} - -/* For Firefox */ -.messages-area { - scrollbar-width: thin; /* Make scrollbar thinner */ - scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-bg); /* Thumb and track colors */ -} - -/* General Styles */ -.chat-container { - display: flex; - flex-direction: column; - height: 75vh; - /*max-height: 100vh;*/ - max-width: 600px; - margin: auto; - border: 1px solid #ccc; - border-radius: 8px; - overflow: hidden; - background-color: var(--chat-bg); - font-family: var(--font-family); /* Apply the default font family */ - color: var(--font-color); /* Apply the default font color */ -} - -.disclaimer { - font-size: 0.7em; - text-align: right; - padding: 5px 20px 5px 5px; - margin-bottom: 5px; -} - -.messages-area { - flex: 1; - overflow-y: auto; - padding: 10px; - background-color: var(--bot-message-bg); -} - -.message { - max-width: 90%; - margin-bottom: 10px; - padding: 10px; - border-radius: 15px; - font-size: 1rem; -} - -.message.user { - margin-left: auto; - background-color: var(--user-message-bg); - color: var(--user-message-font-color); /* Apply user message font color */ -} - -.message.bot { - background-color: var(--bot-message-bg); - color: var(--bot-message-font-color); /* Apply bot message font color */ -} - -.message-icons { - display: flex; - align-items: center; -} - -/* Scoped styles for thumb icons */ -.thumb-icon.outlined { - color: var(--thumb-icon-outlined); /* Color for outlined state */ -} - -.thumb-icon.filled { - color: var(--thumb-icon-filled); /* Color for filled state */ -} - -/* Default styles for material icons */ -.material-icons { - font-size: 24px; - vertical-align: middle; - cursor: pointer; -} - -.question-area { - display: flex; - flex-direction: row; - align-items: center; - background-color: var(--user-message-bg); - padding: 10px; -} - -.language-select-container { - width: 100%; - margin-bottom: 10px; /* Spacing between the dropdown and the textarea */ -} - -.language-select { - width: 100%; - margin-bottom: 5px; /* Space between the dropdown and the send button */ - padding: 8px; - border-radius: 5px; - border: 1px solid var(--input-border); - background-color: var(--input-bg); - color: var(--input-text-color); - font-size: 1rem; -} - -.question-area textarea { - flex: 1; - border: none; - padding: 10px; - border-radius: 15px; - background-color: var(--input-bg); - border: 1px solid var(--input-border); - color: var(--input-text-color); - font-family: var(--font-family); /* Apply the default font family */ - font-size: 1rem; - resize: vertical; - min-height: 60px; - max-height: 150px; - overflow-y: auto; - margin-right: 10px; /* Space between textarea and right-side container */ -} - -.right-side { - display: flex; - flex-direction: column; - align-items: center; -} - -.question-area button { - background: none; - border: none; - cursor: pointer; - color: var(--button-color); -} - -/* Styles for the send icon */ -.send-icon { - font-size: 24px; - color: var(--button-color); - cursor: pointer; -} - -.send-icon.disabled { - color: grey; /* Color for the disabled state */ - cursor: not-allowed; /* Change cursor to indicate disabled state */ -} - -/* New CSS for the status-line */ -.status-line { - height: var(--status-line-height); /* Fixed height for the status line */ - padding: 5px 10px; - background-color: var(--status-line-bg); /* Background color */ - color: var(--status-line-color); /* Text color */ - font-size: 0.9rem; /* Slightly smaller font size */ - text-align: center; /* Centered text */ - border-top: 1px solid #ccc; /* Subtle top border */ - display: flex; - align-items: center; - justify-content: flex-start; -} - -/* Algorithm-specific colors for fingerprint icon */ -.fingerprint-rag-tenant { - color: var(--algorithm-color-rag-tenant); -} - -.fingerprint-rag-wikipedia { - color: var(--algorithm-color-rag-wikipedia); -} - -.fingerprint-rag-google { - color: var(--algorithm-color-rag-google); -} - -.fingerprint-llm { - color: var(--algorithm-color-llm); -} - -/* Styling for citation links */ -.citations a { - background-color: var(--link-bg); /* Apply default background color */ - color: var(--link-color); /* Apply default link color */ - padding: 2px 4px; /* Add padding for better appearance */ - border-radius: 3px; /* Add slight rounding for a modern look */ - text-decoration: none; /* Remove default underline */ - transition: background-color 0.3s, color 0.3s; /* Smooth transition for hover effects */ -} - -.citations a:hover { - background-color: var(--link-hover-bg); /* Background color on hover */ - color: var(--link-hover-color); /* Text color on hover */ -} - -/* Media queries for responsiveness */ -@media (max-width: 768px) { - .chat-container { - max-width: 90%; /* Reduce max width on smaller screens */ - } -} - -@media (max-width: 480px) { - .chat-container { - max-width: 95%; /* Further reduce max width on very small screens */ - } - - .question-area input { - font-size: 0.9rem; /* Adjust input font size for smaller screens */ - } - - .status-line { - font-size: 0.8rem; /* Adjust status line font size for smaller screens */ - } -} - - - diff --git a/nginx/static/js/eveai-chat-widget.js b/nginx/static/js/eveai-chat-widget.js deleted file mode 100644 index 0eef8eb..0000000 --- a/nginx/static/js/eveai-chat-widget.js +++ /dev/null @@ -1,470 +0,0 @@ -class EveAIChatWidget extends HTMLElement { - static get observedAttributes() { - return ['tenant-id', 'api-key', 'domain', 'language', 'languages']; - } - - constructor() { - super(); - this.socket = null; // Initialize socket to null - this.attributesSet = false; // Flag to check if all attributes are set - this.jwtToken = null; // Initialize jwtToken to null - this.userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; // Detect user's timezone - this.heartbeatInterval = null; - this.idleTime = 0; // in milliseconds - this.maxConnectionIdleTime = 1 * 60 * 60 * 1000; // 1 hours in milliseconds - this.languages = [] - this.room = null; - console.log('EveAIChatWidget constructor called'); - } - - connectedCallback() { - console.log('connectedCallback called'); - this.innerHTML = this.getTemplate(); - this.messagesArea = this.querySelector('.messages-area'); - this.questionInput = this.querySelector('.question-area textarea'); - this.sendButton = this.querySelector('.send-icon'); - this.languageSelect = this.querySelector('.language-select'); - this.statusLine = this.querySelector('.status-line'); - this.statusMessage = this.querySelector('.status-message'); - this.connectionStatusIcon = this.querySelector('.connection-status-icon'); - - this.sendButton.addEventListener('click', () => this.handleSendMessage()); - this.questionInput.addEventListener('keydown', (event) => { - if (event.key === 'Enter' && !event.shiftKey) { - event.preventDefault(); // Prevent adding a new line - this.handleSendMessage(); - } - }); - - if (this.areAllAttributesSet() && !this.socket) { - console.log('Attributes already set in connectedCallback, initializing socket'); - this.initializeSocket(); - } - } - - populateLanguageDropdown() { - // Clear existing options - this.languageSelect.innerHTML = ''; - console.log(`languages for options: ${this.languages}`) - - // Populate with new options - this.languages.forEach(lang => { - const option = document.createElement('option'); - option.value = lang; - option.textContent = lang.toUpperCase(); - if (lang === this.currentLanguage) { - option.selected = true; - } - console.log(`Adding option for language: ${lang}`) - this.languageSelect.appendChild(option); - }); - - // Add event listener for language change - this.languageSelect.addEventListener('change', (e) => { - this.currentLanguage = e.target.value; - // You might want to emit an event or update the backend about the language change - }); - } - - attributeChangedCallback(name, oldValue, newValue) { - console.log(`attributeChangedCallback called: ${name} changed from ${oldValue} to ${newValue}`); - this.updateAttributes(); - - if (this.areAllAttributesSet() && !this.socket) { - console.log('All attributes set in attributeChangedCallback, initializing socket'); - this.attributesSet = true; - console.log('All attributes are set, populating language dropdown'); - this.populateLanguageDropdown(); - console.log('All attributes are set, initializing socket') - this.initializeSocket(); - } - } - - updateAttributes() { - this.tenantId = this.getAttribute('tenant-id'); - this.apiKey = this.getAttribute('api-key'); - this.domain = this.getAttribute('domain'); - this.language = this.getAttribute('language'); - const languageAttr = this.getAttribute('languages'); - this.languages = languageAttr ? languageAttr.split(',') : []; - this.currentLanguage = this.language; - console.log('Updated attributes:', { - tenantId: this.tenantId, - apiKey: this.apiKey, - domain: this.domain, - language: this.language, - currentLanguage: this.currentLanguage, - languages: this.languages - }); - } - - areAllAttributesSet() { - const tenantId = this.getAttribute('tenant-id'); - const apiKey = this.getAttribute('api-key'); - const domain = this.getAttribute('domain'); - const language = this.getAttribute('language'); - const languages = this.getAttribute('languages'); - console.log('Checking if all attributes are set:', { - tenantId, - apiKey, - domain, - language, - languages - }); - return tenantId && apiKey && domain && language && languages; - } - - createLanguageDropdown() { - const select = document.createElement('select'); - select.id = 'languageSelect'; - this.languages.forEach(lang => { - const option = document.createElement('option'); - option.value = lang; - option.textContent = lang.toUpperCase(); - if (lang === this.currentLanguage) { - option.selected = true; - } - select.appendChild(option); - }); - select.addEventListener('change', (e) => { - this.currentLanguage = e.target.value; - // You might want to emit an event or update the backend about the language change - }); - return select; - } - - initializeSocket() { - if (this.socket) { - console.log('Socket already initialized'); - return; - } - - console.log(`Initializing socket connection to Evie`); - - // Ensure apiKey is passed in the query parameters - this.socket = io('https://evie.askeveai.com', { - path: '/chat/socket.io/', - transports: ['websocket', 'polling'], - query: { - tenantId: this.tenantId, - apiKey: this.apiKey // Ensure apiKey is included here - }, - auth: { - token: 'Bearer ' + this.apiKey // Ensure token is included here - }, - reconnectionAttempts: Infinity, // Infinite reconnection attempts - reconnectionDelay: 5000, // Delay between reconnections - timeout: 20000 // Connection timeout - }); - - console.log(`Finished initializing socket connection to Evie`); - - this.socket.on('connect', (data) => { - console.log('Socket connected OK'); - this.setStatusMessage('Connected to EveAI.'); - this.updateConnectionStatus(true); - this.startHeartbeat(); - if (data.room) { - this.room = data.room; - console.log(`Joined room: ${this.room}`); - } - }); - - this.socket.on('authenticated', (data) => { - console.log('Authenticated event received: ', data); - this.setStatusMessage('Authenticated.'); - if (data.token) { - this.jwtToken = data.token; // Store the JWT token received from the server - } - if (data.room) { - this.room = data.room; - console.log(`Confirmed room: ${this.room}`); - } - }); - - this.socket.on('connect_error', (err) => { - console.error('Socket connection error:', err); - this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.'); - this.updateConnectionStatus(false); - }); - - this.socket.on('connect_timeout', () => { - console.error('Socket connection timeout'); - this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.'); - this.updateConnectionStatus(false); - }); - - this.socket.on('disconnect', (reason) => { - console.log('Socket disconnected: ', reason); - if (reason === 'io server disconnect') { - // Server disconnected the socket - this.socket.connect(); // Attempt to reconnect - } - this.setStatusMessage('Disconnected from EveAI. Please refresh the page for further interaction.'); - this.updateConnectionStatus(false); - this.stopHeartbeat(); - }); - - this.socket.on('reconnect_attempt', () => { - console.log('Attempting to reconnect to the server...'); - this.setStatusMessage('Attempting to reconnect...'); - }); - - this.socket.on('reconnect', () => { - console.log('Successfully reconnected to the server'); - this.setStatusMessage('Reconnected to EveAI.'); - this.updateConnectionStatus(true); - this.startHeartbeat(); - }); - - 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...'); - } - }); - - this.socket.on('task_status', (data) => { - 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') { - this.updateProgress(); - setTimeout(() => this.checkTaskStatus(data.taskId), 1000); // Poll every second - } else if (data.status === 'success') { - this.addBotMessage(data.answer, data.interaction_id, data.algorithm, data.citations); - this.clearProgress(); // Clear progress indicator when done - } else { - this.setStatusMessage('Failed to process message.'); - } - }); - } - - setStatusMessage(message) { - this.statusMessage.textContent = message; - } - - updateConnectionStatus(isConnected) { - if (isConnected) { - this.connectionStatusIcon.textContent = 'link'; - this.connectionStatusIcon.classList.remove('status-disconnected'); - this.connectionStatusIcon.classList.add('status-connected'); - } else { - this.connectionStatusIcon.textContent = 'link_off'; - this.connectionStatusIcon.classList.remove('status-connected'); - this.connectionStatusIcon.classList.add('status-disconnected'); - } - } - - startHeartbeat() { - this.stopHeartbeat(); // Clear any existing interval - this.heartbeatInterval = setInterval(() => { - if (this.socket && this.socket.connected) { - this.socket.emit('heartbeat'); - this.idleTime += 30000; - if (this.idleTime >= this.maxConnectionIdleTime) { - this.socket.disconnect(); - this.setStatusMessage('Disconnected due to inactivity.'); - this.updateConnectionStatus(false); - this.stopHeartbeat(); - } - } - }, 30000); // Send a heartbeat every 30 seconds - } - - stopHeartbeat() { - if (this.heartbeatInterval) { - clearInterval(this.heartbeatInterval); - this.heartbeatInterval = null; - } - } - - updateProgress() { - if (!this.statusMessage.textContent) { - this.statusMessage.textContent = 'Processing...'; - } else { - this.statusMessage.textContent += '.'; // Append a dot - } - } - - clearProgress() { - this.statusMessage.textContent = ''; - this.toggleSendButton(false); // Re-enable and revert send button to outlined version - } - - checkTaskStatus(taskId) { - this.updateProgress(); - this.socket.emit('check_task_status', { task_id: taskId }); - } - - getTemplate() { - return ` -
-
-
Evie can make mistakes. Please double-check responses.
-
- -
- - send -
-
-
- link_off - -
-
- `; - } - - addUserMessage(text) { - const message = document.createElement('div'); - message.classList.add('message', 'user'); - message.innerHTML = `

${text}

`; - this.messagesArea.appendChild(message); - 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); - let citationsHtml = citations.map(url => `${url}`).join('
'); - - 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 'LLM': - algorithmClass = 'fingerprint-llm'; - break; - default: - algorithmClass = ''; - } - - message.innerHTML = ` -

${content}

- ${citationsHtml ? `

${citationsHtml}

` : ''} -
- fingerprint - thumb_up_off_alt - thumb_down_off_alt -
- `; - 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) { - console.log('feedback called'); - this.idleTime = 0; // Reset idle time - 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'); - this.idleTime = 0; // Reset idle time - const message = this.questionInput.value.trim(); - if (message) { - this.addUserMessage(message); - this.questionInput.value = ''; - this.sendMessageToBackend(message); - this.toggleSendButton(true); // Disable and change send button to filled version - } - } - - sendMessageToBackend(message) { - console.log('sendMessageToBackend called'); - if (!this.socket) { - console.error('Socket is not initialized'); - return; - } - if (!this.jwtToken) { - console.error('JWT token is not available'); - return; - } - - const selectedLanguage = this.languageSelect.value; - - console.log('Sending message to backend'); - this.socket.emit('user_message', { - tenantId: this.tenantId, - token: this.jwtToken, - message, - language: selectedLanguage, - timezone: this.userTimezone - }); - 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); - diff --git a/nginx/static/js/eveai-sdk.js b/nginx/static/js/eveai-sdk.js deleted file mode 100644 index 1e9ccb8..0000000 --- a/nginx/static/js/eveai-sdk.js +++ /dev/null @@ -1,29 +0,0 @@ -// static/js/eveai-sdk.js -class EveAI { - constructor(tenantId, apiKey, domain, language, languages) { - this.tenantId = tenantId; - this.apiKey = apiKey; - this.domain = domain; - this.language = language; - this.languages = languages; - - console.log('EveAI constructor:', { tenantId, apiKey, domain, language, languages }); - } - - initializeChat(containerId) { - const container = document.getElementById(containerId); - if (container) { - container.innerHTML = ''; - customElements.whenDefined('eveai-chat-widget').then(() => { - const chatWidget = container.querySelector('eveai-chat-widget'); - chatWidget.setAttribute('tenant-id', this.tenantId); - chatWidget.setAttribute('api-key', this.apiKey); - chatWidget.setAttribute('domain', this.domain); - chatWidget.setAttribute('language', this.language); - chatWidget.setAttribute('languages', this.languages); - }); - } else { - console.error('Container not found'); - } - } -} \ No newline at end of file