|
|
|
let sessionId = null; |
|
let models = []; |
|
let currentModel = null; |
|
|
|
const REQUEST_LIMIT = 3; |
|
const REQUEST_WINDOW_MS = 30 * 1000; |
|
let requestTimestamps = []; |
|
|
|
|
|
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; |
|
|
|
|
|
function canSendRequest() { |
|
const now = Date.now(); |
|
|
|
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; |
|
}); |
|
|
|
|
|
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 || []; |
|
|
|
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; |
|
|
|
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; |
|
}; |
|
|
|
|
|
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, |
|
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'); |
|
} |
|
}; |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
window.addEventListener('beforeunload', logoutAndReset); |
|
|
|
|
|
apiKeyInput.addEventListener('keyup', function(e) { |
|
if (e.key === 'Enter') loginBtn.click(); |
|
}); |
|
|