// --- CONFIGURATION --- // Set this to your Railway URL let BACKEND_URL = "https://prod-infinit-memory-production.up.railway.app"; // Safety: Remove trailing slash if the user adds one if (BACKEND_URL.endsWith('/')) BACKEND_URL = BACKEND_URL.slice(0, -1); const chatContainer = document.getElementById('chat-container'); const userInput = document.getElementById('user-input'); const sendBtn = document.getElementById('send-btn'); const statsDisplay = document.getElementById('pipeline-stats'); // BYOK Elements const settingsToggle = document.getElementById('settings-toggle'); const settingsPanel = document.getElementById('settings-panel'); const apiKeyInput = document.getElementById('api-key-input'); const llmModelInput = document.getElementById('llm-model-input'); const embedModelInput = document.getElementById('embed-model-input'); // Load saved settings from localStorage apiKeyInput.value = localStorage.getItem('byok_api_key') || ''; llmModelInput.value = localStorage.getItem('byok_llm_model') || 'openai/gpt-4o-mini'; embedModelInput.value = localStorage.getItem('byok_embed_model') || 'qwen/qwen3-embedding-8b'; // Toggle Settings Panel settingsToggle.addEventListener('click', () => { settingsPanel.classList.toggle('active'); }); /** * Adds a message bubble to the chat container */ function addMessage(text, role) { // Remove welcome message if it exists const welcome = document.querySelector('.welcome-message'); if (welcome) welcome.remove(); const msgDiv = document.createElement('div'); msgDiv.classList.add('message', `${role}-message`); msgDiv.textContent = text; chatContainer.appendChild(msgDiv); chatContainer.scrollTop = chatContainer.scrollHeight; return msgDiv; } /** * Adds a visual typing indicator */ function addLoadingIndicator() { const loader = document.createElement('div'); loader.classList.add('message', 'bot-message', 'loading-msg'); loader.innerHTML = `
`; chatContainer.appendChild(loader); chatContainer.scrollTop = chatContainer.scrollHeight; return loader; } /** * Main function to send message and get BYOK response */ async function sendMessage() { const text = userInput.value.trim(); if (!text) return; // Persist settings to localStorage localStorage.setItem('byok_api_key', apiKeyInput.value); localStorage.setItem('byok_llm_model', llmModelInput.value); localStorage.setItem('byok_embed_model', embedModelInput.value); // Clear input and add user message userInput.value = ''; userInput.style.height = 'auto'; addMessage(text, 'user'); // Add loading indicator const loader = addLoadingIndicator(); try { const response = await fetch(`${BACKEND_URL}/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: text, api_key: apiKeyInput.value || null, llm_model: llmModelInput.value || null, embed_model: embedModelInput.value || null }) }); const data = await response.json(); // Remove loader and add bot response loader.remove(); if (data.answer) { addMessage(data.answer, 'bot'); // Update performance stats const pipeTime = data.total_time ? data.total_time.toFixed(1) : '--'; const retTime = data.timings && data.timings.retrieval ? data.timings.retrieval.toFixed(2) : '--'; statsDisplay.textContent = `Pipeline: ${pipeTime}s | Retrieval: ${retTime}s`; } else { addMessage('Error: No response from engine.', 'bot'); } } catch (err) { if (loader) loader.remove(); addMessage(`Error: ${err.message}`, 'bot'); } } /** * Helper for suggestion buttons */ function useSuggestion(text) { userInput.value = text; sendMessage(); } // Event Listeners sendBtn.addEventListener('click', sendMessage); userInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); // Auto-resize textarea as user types userInput.addEventListener('input', function () { this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px'; });