Multi-ai-chat / static /index.html
Bc-AI's picture
Update static/index.html
bd029ca verified
Raw
History Blame Contribute Delete
13 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nova-2-Nano — Pure NTA Think Twice</title>
<style>
:root { --cyan: #00FFFF; --pink: #FF69B4; --dark: #0a0a0f; --card: #12121a; --border: #1e1e2e; --text: #e0e0e0; --muted: #888; }
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Inter', sans-serif; background: var(--dark); color: var(--text); min-height: 100vh; }
.bg-glow { position: fixed; top: -50%; left: -50%; width: 200%; height: 200%; background: radial-gradient(circle at 30% 40%, rgba(0,255,255,0.03) 0%, transparent 50%), radial-gradient(circle at 70% 60%, rgba(255,105,180,0.03) 0%, transparent 50%); animation: pulse 8s ease-in-out infinite; z-index: -1; }
@keyframes pulse { 0%, 100% { transform: scale(1); opacity: 0.5; } 50% { transform: scale(1.1); opacity: 0.8; } }
.container { max-width: 900px; margin: 0 auto; padding: 2rem 1rem; }
header { text-align: center; margin-bottom: 2rem; }
.logo { font-size: 2.5rem; font-weight: 800; background: linear-gradient(135deg, var(--cyan), var(--pink)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.subtitle { color: var(--muted); font-family: monospace; font-size: 0.9rem; letter-spacing: 2px; }
.chat-container { background: var(--card); border: 1px solid var(--border); border-radius: 16px; overflow: hidden; box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
.messages { height: 500px; overflow-y: auto; padding: 1.5rem; display: flex; flex-direction: column; gap: 1rem; }
.message { max-width: 95%; padding: 1rem; border-radius: 12px; line-height: 1.6; animation: fadeIn 0.3s ease; background: rgba(0,0,0,0.2); border: 1px solid var(--border); }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
pre { white-space: pre-wrap; font-family: 'JetBrains Mono', monospace; margin: 0; font-size: 0.95rem; }
.prompt-text { color: var(--cyan); }
.gen-text { color: var(--text); }
.message-meta { font-size: 0.75rem; color: var(--muted); margin-top: 0.75rem; font-family: monospace; border-top: 1px solid var(--border); padding-top: 0.5rem; }
.confidence-bar { height: 3px; background: var(--border); border-radius: 2px; margin-top: 0.5rem; overflow: hidden; }
.confidence-fill { height: 100%; background: linear-gradient(90deg, var(--cyan), var(--pink)); transition: width 0.5s ease; }
.input-area { padding: 1rem 1.5rem; border-top: 1px solid var(--border); display: flex; gap: 0.75rem; align-items: flex-end; }
textarea { flex: 1; background: var(--dark); border: 1px solid var(--border); border-radius: 12px; padding: 0.75rem 1rem; color: var(--text); font-family: 'JetBrains Mono', monospace; font-size: 0.95rem; resize: none; min-height: 80px; outline: none; }
textarea:focus { border-color: var(--cyan); }
button.send { background: linear-gradient(135deg, var(--cyan), var(--pink)); border: none; border-radius: 12px; padding: 0.75rem 1.5rem; color: var(--dark); font-weight: 700; cursor: pointer; height: 48px; }
button.send:disabled { opacity: 0.5; cursor: not-allowed; }
.controls { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 1rem; padding: 1rem 1.5rem; border-top: 1px solid var(--border); background: rgba(0,0,0,0.2); }
.control-group label { display: block; font-size: 0.75rem; color: var(--muted); margin-bottom: 0.25rem; text-transform: uppercase; }
select, input[type="range"] { width: 100%; background: var(--dark); border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem; color: var(--text); }
.toggle { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; }
.toggle input { accent-color: var(--cyan); width: 16px; height: 16px; }
.status-bar { display: flex; justify-content: space-between; padding: 0.75rem 1.5rem; background: rgba(0,0,0,0.3); font-family: monospace; font-size: 0.8rem; color: var(--muted); }
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: #4ade80; display: inline-block; margin-right: 0.5rem; animation: blink 2s infinite; }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
.arch-badge { display: inline-block; background: rgba(0,255,255,0.1); border: 1px solid var(--cyan); border-radius: 6px; padding: 0.25rem 0.5rem; font-size: 0.7rem; color: var(--cyan); margin-top: 0.5rem; font-family: monospace; }
</style>
</head>
<body>
<div class="bg-glow"></div>
<div class="container">
<header>
<div class="logo">🧠 Nova-2-Nano</div>
<div class="subtitle">PURE NTA • 304M PARAMS • THINK TWICE</div>
<div class="arch-badge">12x NovaTripleAttention | 1024-d | PPL 1.0</div>
</header>
<div class="chat-container">
<div class="messages" id="messages">
<div class="message">
<pre><span class="prompt-text">Nova-2-Nano is ready. Pure NTA architecture with internal ThinkTwice.</span></pre>
<div class="message-meta">Type a prompt below. Toggle ThinkTwice to see per-layer confidence re-computation.</div>
</div>
</div>
<div class="controls">
<div class="control-group">
<label>Max Tokens: <span id="maxTokensVal">150</span></label>
<input type="range" id="maxTokens" min="10" max="512" value="150" step="10">
</div>
<div class="control-group">
<label>Temp: <span id="tempVal">0.7</span></label>
<input type="range" id="temperature" min="0.1" max="1.5" value="0.7" step="0.1">
</div>
<div class="control-group">
<label>Think Twice</label>
<div class="toggle">
<input type="checkbox" id="thinkTwice" checked>
<span>Per-Layer</span>
</div>
</div>
</div>
<div class="input-area">
<textarea id="input" placeholder="Type your prompt here...">def reverse_string(s):
return </textarea>
<button class="send" id="sendBtn">Generate 🚀</button>
</div>
<div class="status-bar">
<span><span class="status-dot"></span><span id="statusText">Connected</span></span>
<span id="statsText">Triggers: — | Conf: —</span>
</div>
</div>
</div>
<script>
const API_BASE = window.location.origin;
const messagesEl = document.getElementById('messages');
const inputEl = document.getElementById('input');
const sendBtn = document.getElementById('sendBtn');
const statusText = document.getElementById('statusText');
const statsText = document.getElementById('statsText');
inputEl.addEventListener('input', () => {
inputEl.style.height = 'auto';
inputEl.style.height = Math.min(inputEl.scrollHeight, 200) + 'px';
});
document.getElementById('maxTokens').addEventListener('input', e => document.getElementById('maxTokensVal').textContent = e.target.value);
document.getElementById('temperature').addEventListener('input', e => document.getElementById('tempVal').textContent = e.target.value);
async function sendMessage() {
const text = inputEl.value;
if (!text) return;
inputEl.value = '';
inputEl.style.height = 'auto';
sendBtn.disabled = true;
statusText.textContent = 'Generating...';
const outputDiv = document.createElement('div');
outputDiv.className = 'message';
const safePrompt = text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
outputDiv.innerHTML = `<pre><span class="prompt-text">${safePrompt}</span><span class="gen-text"></span></pre><div class="message-meta" id="live-stats">🧠 Generating...</div>`;
messagesEl.appendChild(outputDiv);
messagesEl.scrollTop = messagesEl.scrollHeight;
const genTextEl = outputDiv.querySelector('.gen-text');
const statsEl = outputDiv.querySelector('#live-stats');
let fullText = "";
try {
const response = await fetch(`${API_BASE}/v1/completions/stream`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
prompt: text,
max_tokens: parseInt(document.getElementById('maxTokens').value),
temperature: parseFloat(document.getElementById('temperature').value),
think_twice: document.getElementById('thinkTwice').checked
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
if (line.startsWith('event:')) {
var eventType = line.replace('event:', '').trim();
} else if (line.startsWith('data:')) {
const dataStr = line.replace('data:', '').trim();
if (!dataStr) continue;
try {
const data = JSON.parse(dataStr);
if (eventType === 'token') {
fullText += data.text;
genTextEl.innerHTML = fullText.replace(/</g, "&lt;").replace(/>/g, "&gt;");
messagesEl.scrollTop = messagesEl.scrollHeight;
} else if (eventType === 'thinking') {
statsEl.innerHTML = `🧠 Re-computing... (Layer Conf: ${(data.avg_layer_conf * 100).toFixed(0)}%)`;
statsEl.style.color = 'var(--pink)';
} else if (eventType === 'done') {
statsEl.innerHTML = `🧠 ThinkTwice Triggers: ${data.think_twice_triggers} | Conf: ${(data.final_confidence * 100).toFixed(0)}% | ${data.latency_ms.toFixed(0)}ms`;
statsEl.style.color = 'var(--muted)';
statsText.textContent = `Triggers: ${data.think_twice_triggers} | Conf: ${(data.final_confidence * 100).toFixed(0)}%`;
const bar = document.createElement('div');
bar.className = 'confidence-bar';
const fill = document.createElement('div');
fill.className = 'confidence-fill';
fill.style.width = `${Math.round(data.final_confidence * 100)}%`;
bar.appendChild(fill);
outputDiv.appendChild(bar);
}
} catch (e) { console.error(e); }
}
}
}
statusText.textContent = 'Ready';
} catch (err) {
genTextEl.innerHTML += `<br>❌ Error: ${err.message}`;
statusText.textContent = 'Error';
} finally {
sendBtn.disabled = false;
inputEl.focus();
}
}
sendBtn.addEventListener('click', sendMessage);
inputEl.addEventListener('keydown', e => {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
sendMessage();
}
});
fetch(`${API_BASE}/health`).then(r => r.json()).then(d => statusText.textContent = d.model_loaded ? 'Model Loaded ✅' : 'Loading...').catch(() => statusText.textContent = 'Disconnected ❌');
</script>
</body>
</html>