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(); | |
}); | |