diff --git a/chatbot/js/chatbot.js b/chatbot/js/chatbot.js index 9c9c221e6ac39a115f04bebff3afc3b3e80d80e8..0619a21a7d99fbfc9eb7de8abc45fc3c2f546560 100644 --- a/chatbot/js/chatbot.js +++ b/chatbot/js/chatbot.js @@ -8,11 +8,28 @@ const chatbox = document.querySelector(".chatbox"); const chatInput = document.querySelector(".chat-input textarea"); const sendChatBtn = document.querySelector("#send-btn"); - const micBtn = document.querySelector("#mic-btn"); + //const micBtn = document.querySelector("#mic-btn"); const resizeHandle = document.querySelector(".resize-handle"); - if (!chatbot || !chatbotToggler || !closeBtn || !chatbox || - !chatInput || !sendChatBtn || !micBtn || !resizeHandle) { + // Check if all required elements exist + const elementsToCheck = { + "chatbot": chatbot, + "chatbotToggler": chatbotToggler, + "closeBtn": closeBtn, + "chatbox": chatbox, + "chatInput": chatInput, + "sendChatBtn": sendChatBtn, + //"micBtn": micBtn, + "resizeHandle": resizeHandle + }; + + for (const [name, element] of Object.entries(elementsToCheck)) { + if (!element) { + console.error(`Missing element: ${name}`); + } + } + + if (!chatbot || !chatbotToggler || !closeBtn || !chatbox || !chatInput || !sendChatBtn || /* !micBtn || */ !resizeHandle) { console.error("One or more chatbot elements not found"); return; } @@ -35,9 +52,9 @@ } } - if (!settings.enable_speech_to_text) { + /* if (!settings.enable_speech_to_text) { micBtn.style.display = 'none'; - } + } */ // Update initial timestamp for the welcome message updateInitialTimestamp(); @@ -49,7 +66,7 @@ let GROQ_MODEL = 'llama3-70b-8192'; let GEMINI_MODEL = 'gemini-2.0-flash'; - // First try to get settings from drupalSettings + // First try to get settings from drupalSettings if (drupalSettings && drupalSettings.chatbot) { console.log('Loading settings from drupalSettings:', drupalSettings.chatbot); API_KEY = drupalSettings.chatbot.api_key; @@ -64,24 +81,24 @@ if (!API_KEY || !API_URL) { console.log('Settings not found in drupalSettings, fetching from API...'); fetch('/api/chatbot/settings') - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok ' + response.statusText); - } - return response.json(); - }) - .then(data => { - API_KEY = data.api_key; - API_URL = data.endpoint; - API_PROVIDER = data.api_provider || 'openai'; - OPENAI_MODEL = data.openai_model || 'gpt-4'; - GROQ_MODEL = data.groq_model || 'llama3-70b-8192'; - GEMINI_MODEL = data.gemini_model || 'gemini-2.0-flash'; - console.log('Chatbot settings loaded successfully. Using provider:', API_PROVIDER); - }) - .catch(error => { - console.error('Error fetching chatbot settings:', error); - }); + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok ' + response.statusText); + } + return response.json(); + }) + .then(data => { + API_KEY = data.api_key; + API_URL = data.endpoint; + API_PROVIDER = data.api_provider || 'openai'; + OPENAI_MODEL = data.openai_model || 'gpt-4'; + GROQ_MODEL = data.groq_model || 'llama3-70b-8192'; + GEMINI_MODEL = data.gemini_model || 'gemini-2.0-flash'; + console.log('Chatbot settings loaded successfully. Using provider:', API_PROVIDER); + }) + .catch(error => { + console.error('Error fetching chatbot settings:', error); + }); } let userMessage = null; @@ -134,27 +151,6 @@ return chatLi; }; - // Create a typing indicator - const createTypingIndicator = () => { - const chatLi = document.createElement("li"); - chatLi.classList.add("chat", "incoming"); - - const currentTime = getCurrentTime(); - chatLi.innerHTML = ` - <div class="bot-icon-container"> - <span class="material-symbols-outlined">smart_toy</span> - <div class="bot-label">GPTBot</div> - <div class="bot-timestamp">${currentTime}</div> - </div> - <div class="typing-indicator"> - <span></span> - <span></span> - <span></span> - </div> - `; - return chatLi; - }; - // Function to prepare request body based on the selected API provider const prepareRequestBody = (message) => { switch (API_PROVIDER) { @@ -170,8 +166,15 @@ }; case 'gemini': return { - contents: [{role: "user", parts: [{text: message}]}], - model: GEMINI_MODEL, + contents: [ + { + role: "user", + parts: [ + {text: message} + ] + } + ] + // Not adding model here, it goes in the URL for Gemini }; default: return { @@ -181,15 +184,21 @@ } }; - // Function to extract response from API based on provider const extractResponse = (data) => { + console.log("Raw response data:", data); + switch (API_PROVIDER) { case 'openai': return data.choices[0].message.content.trim(); case 'groq': return data.choices[0].message.content.trim(); case 'gemini': - return data.candidates[0].content.parts[0].text.trim(); + if (data.candidates && data.candidates[0] && data.candidates[0].content) { + return data.candidates[0].content.parts[0].text.trim(); + } else { + console.error("Unexpected Gemini response structure:", data); + throw new Error("Invalid Gemini response format"); + } default: return data.choices[0].message.content.trim(); } @@ -206,42 +215,64 @@ typingIndicator.classList.add("typing-indicator"); typingIndicator.innerHTML = "<span></span><span></span><span></span>"; messageElement.appendChild(typingIndicator); - - - + + // Check if API settings are loaded if (!API_KEY || !API_URL) { console.error("API settings not loaded. API_KEY:", !!API_KEY, "API_URL:", !!API_URL); + messageElement.innerHTML = ""; // Clear typing indicator messageElement.textContent = "Chatbot is not configured properly. Please try again later."; messageElement.classList.add("error"); return; } - + + let url; const requestOptions = { method: "POST", headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${API_KEY}` + "Content-Type": "application/json" }, body: JSON.stringify(prepareRequestBody(userMessage)) }; - - fetch(API_URL, requestOptions) + + // Handle different API providers + if (API_PROVIDER === 'gemini') { + // For Gemini, we need to use the correct Google API URL format + const baseUrl = 'https://generativelanguage.googleapis.com/v1beta/models/' + + encodeURIComponent(GEMINI_MODEL) + ':generateContent'; + url = `${baseUrl}?key=${encodeURIComponent(API_KEY)}`; + + console.log(`Making Gemini API request to ${url.replace(API_KEY, '[REDACTED]')}`); + } else { + // For OpenAI and Groq, we use Bearer token authentication with the provided endpoint + url = API_URL; + requestOptions.headers["Authorization"] = `Bearer ${API_KEY}`; + console.log(`Making ${API_PROVIDER} API request to ${url}`); + } + + // Log request body for debugging + console.log("Request body:", JSON.parse(requestOptions.body)); + + fetch(url, requestOptions) .then(res => { + console.log("API response status:", res.status, res.statusText); if (!res.ok) { - throw new Error(`API responded with status ${res.status}: ${res.statusText}`); + return res.text().then(text => { + console.error("API error response:", text); + throw new Error(`API responded with status ${res.status}: ${res.statusText}`); + }); } return res.json(); }) .then(data => { - try{ // Remove typing indicator messageElement.innerHTML = ""; - messageElement.textContent = extractResponse(data); - } catch (error) { - console.error("Error extracting response:", error,data); - messageElement.classList.add("error"); - messageElement.textContent = "Error processing response from AI provider."; - } + try { + messageElement.textContent = extractResponse(data); + } catch (error) { + console.error('Error extracting response:', error, data); + messageElement.classList.add("error"); + messageElement.textContent = "Error processing response from AI provider."; + } }) .catch((error) => { console.error('API request failed:', error); @@ -266,16 +297,8 @@ // Slight delay before showing the bot's response to allow outgoing animation to finish setTimeout(() => { - // Create incoming chat element with empty text first const incomingChatLi = createChatLi("Thinking...", "incoming"); chatbox.appendChild(incomingChatLi); - - // Add the typing indicator inside the paragraph - const typingIndicator = document.createElement("div"); - typingIndicator.classList.add("typing-indicator"); - typingIndicator.innerHTML = "<span></span><span></span><span></span>"; - incomingChatLi.querySelector("p").appendChild(typingIndicator); - chatbox.scrollTo(0, chatbox.scrollHeight); // Generate response with a slight delay @@ -290,9 +313,9 @@ if (SpeechRecognition) { const recognition = new SpeechRecognition(); - recognition.onstart = () => { + /* recognition.onstart = () => { micBtn.classList.add('listening'); - }; + }; */ recognition.onresult = (event) => { const current = event.resultIndex; @@ -301,15 +324,15 @@ handleChat(); }; - recognition.onend = () => { + /* recognition.onend = () => { micBtn.classList.remove('listening'); }; micBtn.addEventListener('click', () => { recognition.start(); - }); + }); */ } else { - micBtn.style.display = 'none'; + // micBtn.style.display = 'none'; } // Resizing functionality diff --git a/chatbot/src/Controller/HelloController.php b/chatbot/src/Controller/HelloController.php index 275ba8c74e75c324c540071659e88913c6d7eebc..aca3a4868a19b4f3dbed19609518c5b854312541 100644 --- a/chatbot/src/Controller/HelloController.php +++ b/chatbot/src/Controller/HelloController.php @@ -29,7 +29,7 @@ class HelloController extends ControllerBase { 'drupalSettings' => [ 'chatbot' => [ 'api_provider' => $config->get('api_provider') ?: 'openai', - 'api_key' => $config->get('api_key'), + 'apiKey' => $config->get('api_key'), 'endpoint' => $config->get('endpoint'), 'openai_model' => $config->get('openai_model') ?: 'gpt-4', 'groq_model' => $config->get('groq_model') ?: 'llama3-70b-8192', @@ -51,7 +51,7 @@ class HelloController extends ControllerBase { $settings = [ 'api_provider' => $config->get('api_provider') ?: 'openai', - 'api_key' => $config->get('api_key'), + 'apiKey' => $config->get('api_key'), 'endpoint' => $config->get('endpoint'), 'openai_model' => $config->get('openai_model') ?: 'gpt-4', 'groq_model' => $config->get('groq_model') ?: 'llama3-70b-8192', diff --git a/chatbot/src/Plugin/Block/HelloBlock.php b/chatbot/src/Plugin/Block/HelloBlock.php index 39d9074fe86d67e93f86c59700f5ebd941dd0e63..344a1f446c4f7c4cb7f728510eae463a06e14e0e 100644 --- a/chatbot/src/Plugin/Block/HelloBlock.php +++ b/chatbot/src/Plugin/Block/HelloBlock.php @@ -27,7 +27,7 @@ class HelloBlock extends BlockBase implements BlockPluginInterface { $build['#attached']['library'][] = 'chatbot/chatbot'; - $build['chatbot'] = [ + $build['chatbot'] = [ '#theme' => 'chatbot', '#title' => 'chatbot', '#attached' => [ @@ -97,13 +97,13 @@ class HelloBlock extends BlockBase implements BlockPluginInterface { ], '#default_value' => $config['api_provider'] ?? 'openai', '#description' => $this->t('Select which AI provider to use for the chatbot.'), - ]; + ]; $form['api_key'] = [ '#type' => 'textfield', '#title' => $this->t('API Key'), '#default_value' => $config['api_key'] ?? '', - '#description' => $this->t('Enter the API key for the chatbot.'), + '#description' => $this->t('Enter the API key for the selected provider.'), '#required' => TRUE, ]; @@ -111,7 +111,7 @@ class HelloBlock extends BlockBase implements BlockPluginInterface { '#type' => 'textfield', '#title' => $this->t('API Endpoint'), '#default_value' => $config['endpoint'] ?? '', - '#description' => $this->t('Enter the API endpoint for the chatbot.'), + '#description' => $this->t('Enter the API endpoint for the selected provider.'), '#required' => TRUE, ]; @@ -128,7 +128,7 @@ class HelloBlock extends BlockBase implements BlockPluginInterface { $this->configuration['chatbot_color'] = $values['chatbot_color']; $this->configuration['enable_speech_to_text'] = $values['enable_speech_to_text']; - $this->configuration['api_provider'] = $values['api_provider']; + $this->configuration['api_provider'] = $values['api_provider']; $this->configuration['api_key'] = $values['api_key']; $this->configuration['endpoint'] = $values['endpoint']; diff --git a/chatbot/src/Plugin/rest/resource/ChatbotSettingsResource.php b/chatbot/src/Plugin/rest/resource/ChatbotSettingsResource.php index ae1f62d1ae0ea180eaa18238c48bb402054cf2e6..1257637e83787943be14f5e15d5a065489d177b6 100644 --- a/chatbot/src/Plugin/rest/resource/ChatbotSettingsResource.php +++ b/chatbot/src/Plugin/rest/resource/ChatbotSettingsResource.php @@ -3,7 +3,7 @@ namespace Drupal\chatbot\Plugin\rest\resource; use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\rest\Plugin\ResourceBase; +use Drupal\rest\Plugin\ResourceBase; use Drupal\rest\ResourceResponse; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;