Spaces:
Sleeping
Sleeping
| // --- State --- | |
| let sessionId = null; | |
| let models = []; | |
| let currentModel = null; | |
| const REQUEST_LIMIT = 3; | |
| const REQUEST_WINDOW_MS = 30 * 1000; // 1 minute | |
| let requestTimestamps = []; | |
| // --- DOM Elements --- | |
| const modal = document.getElementById('modal'); | |
| const apiKeyInput = document.getElementById('apiKeyInput'); | |
| const loginBtn = document.getElementById('loginBtn'); | |
| const loginError = document.getElementById('loginError'); | |
| const app = document.getElementById('app'); | |
| const chatContainer = document.getElementById('chatContainer'); | |
| const toolToggle = document.getElementById('toolToggle'); | |
| const inputForm = document.getElementById('inputForm'); | |
| const userInput = document.getElementById('userInput'); | |
| const modelSelect = document.getElementById('modelSelect'); | |
| const logoutBtn = document.getElementById('logoutBtn'); | |
| const typingIndicator = document.getElementById('typingIndicator'); | |
| let toolUse = true; | |
| // --- Helpers --- | |
| function canSendRequest() { | |
| const now = Date.now(); | |
| // Remove timestamps older than 1 minute | |
| requestTimestamps = requestTimestamps.filter(ts => now - ts < REQUEST_WINDOW_MS); | |
| return requestTimestamps.length < REQUEST_LIMIT; | |
| } | |
| function recordRequest() { | |
| requestTimestamps.push(Date.now()); | |
| } | |
| function scrollToBottom() { | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } | |
| function addMessage(text, sender) { | |
| const msgDiv = document.createElement('div'); | |
| msgDiv.className = 'message ' + sender; | |
| const bubble = document.createElement('div'); | |
| bubble.className = 'bubble'; | |
| bubble.textContent = text; | |
| msgDiv.appendChild(bubble); | |
| chatContainer.appendChild(msgDiv); | |
| scrollToBottom(); | |
| } | |
| function setTyping(isTyping) { | |
| typingIndicator.classList.toggle('hidden', !isTyping); | |
| scrollToBottom(); | |
| } | |
| function showError(msg) { | |
| loginError.textContent = msg || ''; | |
| } | |
| toolToggle.addEventListener('change', function() { | |
| toolUse = toolToggle.checked; | |
| }); | |
| // --- Auth & Model Selection --- | |
| loginBtn.onclick = async function () { | |
| const apiKey = apiKeyInput.value.trim(); | |
| if (!apiKey) { showError('API key required.'); return; } | |
| loginBtn.disabled = true; | |
| showError(''); | |
| try { | |
| const res = await fetch('/init', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({api_key: apiKey}) | |
| }); | |
| const data = await res.json(); | |
| if (!data.success) { showError(data.error || 'Login failed.'); loginBtn.disabled = false; return; } | |
| sessionId = data.session_id; | |
| models = data.models || []; | |
| // Populate model dropdown | |
| modelSelect.innerHTML = ''; | |
| models.forEach(m => { | |
| const opt = document.createElement('option'); | |
| opt.value = m; | |
| opt.textContent = m; | |
| modelSelect.appendChild(opt); | |
| }); | |
| currentModel = models[0]; | |
| modelSelect.value = currentModel; | |
| // Show app, hide modal | |
| modal.classList.add('hidden'); | |
| app.classList.remove('hidden'); | |
| chatContainer.innerHTML = ''; | |
| userInput.focus(); | |
| } catch (e) { | |
| showError('Network error.'); | |
| loginBtn.disabled = false; | |
| } | |
| }; | |
| modelSelect.onchange = function () { | |
| currentModel = modelSelect.value; | |
| }; | |
| // --- Chat Logic --- | |
| inputForm.onsubmit = async function (e) { | |
| e.preventDefault(); | |
| const text = userInput.value.trim(); | |
| if (!text) return; | |
| if (!canSendRequest()) { | |
| addMessage('[Rate limit] Please wait: only 4 requests per minute allowed.', 'bot'); | |
| return; | |
| } | |
| recordRequest(); | |
| addMessage(text, 'user'); | |
| userInput.value = ''; | |
| setTyping(true); | |
| try { | |
| const res = await fetch('/chat', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({ | |
| session_id: sessionId, | |
| query: text, | |
| tool_use: toolUse, // <-- Use the toggle value here | |
| model: currentModel | |
| }) | |
| }); | |
| const data = await res.json(); | |
| setTyping(false); | |
| if (data.error) { | |
| addMessage('[Error] ' + data.error, 'bot'); | |
| return; | |
| } | |
| addMessage(data.output, 'bot'); | |
| } catch (e) { | |
| setTyping(false); | |
| addMessage('[Network error]', 'bot'); | |
| } | |
| }; | |
| // --- Logout Logic --- | |
| logoutBtn.onclick = async function () { | |
| await logoutAndReset(); | |
| }; | |
| async function logoutAndReset() { | |
| if (sessionId) { | |
| try { | |
| await fetch('/logout', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({session_id: sessionId}) | |
| }); | |
| } catch {} | |
| sessionId = null; | |
| models = []; | |
| currentModel = null; | |
| } | |
| app.classList.add('hidden'); | |
| modal.classList.remove('hidden'); | |
| apiKeyInput.value = ''; | |
| chatContainer.innerHTML = ''; | |
| showError(''); | |
| loginBtn.disabled = false; | |
| } | |
| // --- Auto logout on exit --- | |
| window.addEventListener('beforeunload', logoutAndReset); | |
| // --- UX: Enter key in modal input triggers login --- | |
| apiKeyInput.addEventListener('keyup', function(e) { | |
| if (e.key === 'Enter') loginBtn.click(); | |
| }); | |