| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Guru — Self-Evolving AI</title> |
| <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> |
| <meta http-equiv="Pragma" content="no-cache"> |
| <meta http-equiv="Expires" content="0"> |
| <link rel="icon" type="image/svg+xml" href="/static/favicon.svg"> |
| <style> |
| * { margin: 0; padding: 0; box-sizing: border-box; } |
| body { |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
| background: #f0faf0; |
| color: #1a1a1a; |
| height: 100vh; |
| height: 100dvh; |
| display: flex; |
| flex-direction: column; |
| overflow: hidden; |
| } |
| |
| |
| .disclaimer { |
| background: #d32f2f; color: #fff; |
| padding: 8px 16px; font-size: 13px; text-align: center; line-height: 1.4; |
| } |
| |
| |
| .header { |
| background: #fff; border-bottom: 1px solid #c8e6c9; |
| padding: 12px 16px; |
| display: flex; align-items: center; justify-content: space-between; |
| flex-wrap: wrap; gap: 8px; |
| } |
| .brand { display: flex; align-items: center; gap: 10px; } |
| .brand-icon { |
| width: 36px; height: 36px; background: #2d8a4e; border-radius: 10px; |
| display: flex; align-items: center; justify-content: center; |
| color: #fff; font-weight: 700; font-size: 18px; |
| } |
| .brand-name { font-size: 20px; font-weight: 700; color: #1b5e20; } |
| .brand-sub { font-size: 11px; color: #666; } |
| .brand-sub a { color: #2d8a4e; text-decoration: none; } |
| .badge { |
| background: #e8f5e9; color: #1b5e20; |
| padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; |
| } |
| .status-btn { |
| background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 8px; |
| padding: 5px 12px; font-size: 12px; color: #1b5e20; cursor: pointer; font-weight: 600; |
| } |
| |
| |
| .status { display: none; background: #fff; border-bottom: 1px solid #c8e6c9; padding: 16px; } |
| .status.open { display: block; } |
| .status-grid { |
| display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); |
| gap: 10px; max-width: 700px; margin: 0 auto; |
| } |
| .s-card { background: #e8f5e9; border-radius: 8px; padding: 10px 14px; } |
| .s-label { font-size: 10px; color: #666; text-transform: uppercase; letter-spacing: 0.5px; } |
| .s-val { font-size: 16px; font-weight: 700; color: #1b5e20; margin-top: 2px; } |
| .s-detail { font-size: 10px; color: #888; } |
| |
| |
| .chat { flex: 1; min-height: 0; overflow-y: auto; padding: 16px; max-width: 700px; width: 100%; margin: 0 auto; } |
| .welcome { text-align: center; padding: 40px 16px; color: #666; } |
| .welcome h2 { color: #1b5e20; font-size: 22px; margin-bottom: 8px; } |
| .welcome p { font-size: 14px; line-height: 1.6; max-width: 440px; margin: 0 auto; } |
| |
| .msg { margin-bottom: 12px; max-width: 85%; animation: fadeIn 0.25s ease; } |
| @keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; } } |
| .msg.user { margin-left: auto; } |
| .msg.bot { margin-right: auto; } |
| .bubble { |
| padding: 10px 14px; border-radius: 14px; font-size: 15px; line-height: 1.5; word-wrap: break-word; |
| } |
| .msg.user .bubble { background: #2d8a4e; color: #fff; border-bottom-right-radius: 4px; } |
| .msg.bot .bubble { background: #f1f8e9; color: #1a1a1a; border: 1px solid #c8e6c9; border-bottom-left-radius: 4px; } |
| .meta { font-size: 11px; color: #999; margin-top: 3px; padding: 0 4px; } |
| .msg.user .meta { text-align: right; } |
| |
| |
| .dots::after { content: ''; animation: d 1.5s infinite; } |
| @keyframes d { 0%{content:'.'} 33%{content:'..'} 66%{content:'...'} } |
| |
| |
| .input-bar { |
| background: #fff; border-top: 1px solid #c8e6c9; padding: 12px 16px; |
| flex-shrink: 0; |
| } |
| .input-row { |
| max-width: 700px; margin: 0 auto; display: flex; gap: 8px; |
| } |
| .input-row input { |
| flex: 1; padding: 12px 16px; border: 2px solid #c8e6c9; border-radius: 12px; |
| font-size: 15px; outline: none; background: #f0faf0; |
| } |
| .input-row input:focus { border-color: #2d8a4e; } |
| .input-row button { |
| padding: 12px 20px; background: #2d8a4e; color: #fff; border: none; |
| border-radius: 12px; font-size: 15px; font-weight: 600; cursor: pointer; |
| } |
| .input-row button:hover { background: #1b5e20; } |
| .input-row button:disabled { background: #aaa; cursor: not-allowed; } |
| |
| |
| .footer { |
| text-align: center; padding: 8px; font-size: 11px; color: #888; |
| background: #fff; border-top: 1px solid #c8e6c9; |
| } |
| .footer a { color: #2d8a4e; text-decoration: none; } |
| |
| |
| @media (max-width: 480px) { |
| .msg { max-width: 92%; } |
| .status-grid { grid-template-columns: repeat(2, 1fr); } |
| .s-val { font-size: 14px; } |
| .bubble { font-size: 14px; padding: 9px 12px; } |
| .input-row input { padding: 10px 12px; font-size: 14px; } |
| .input-row button { padding: 10px 16px; font-size: 14px; } |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <div class="disclaimer"> |
| <strong>RESEARCH PREVIEW</strong> — Experimental system. Do not submit personal or sensitive information. All inputs may be stored. No warranties. |
| </div> |
|
|
| <div class="header"> |
| <div class="brand"> |
| <div class="brand-icon" style="background:none;padding:0;"><img src="/static/favicon.svg" width="36" height="36" alt="Guru"></div> |
| <div> |
| <div class="brand-name">Guru</div> |
| <div class="brand-sub">Self-Evolving AI · <a href="https://webmind.sh">webmind.sh</a></div> |
| </div> |
| </div> |
| <div style="display:flex;gap:8px;align-items:center;"> |
| <span class="badge" id="badge">Loading...</span> |
| <button class="status-btn" onclick="toggleStatus()">Status</button> |
| </div> |
| </div> |
|
|
| <div class="status" id="statusPanel"> |
| <div class="status-grid" id="statusGrid"></div> |
| </div> |
|
|
| <div class="chat" id="chat"> |
| <div class="welcome"> |
| <h2>Ask anything</h2> |
| <p>Guru means "teacher" — but right now, Guru is more like a baby who just learned its first words. It has a big name and a small brain. Expect random, confused, or hilariously wrong answers. That's the point: every conversation makes it a little smarter. One day it might live up to the name. Today is not that day.</p> |
| <p style="margin-top:8px;font-size:12px;color:#999;">Research preview — not a product and never will be.</p> |
| <div style="margin-top:20px;display:flex;gap:12px;justify-content:center;flex-wrap:wrap;"> |
| <a href="https://github.com/tejasphatak/webmind-research" target="_blank" style="display:inline-flex;align-items:center;gap:6px;background:#24292e;color:#fff;padding:8px 16px;border-radius:8px;font-size:13px;font-weight:600;text-decoration:none;"> |
| <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg> |
| Star on GitHub |
| </a> |
| <a href="https://huggingface.co/tejadabheja/guru" target="_blank" style="display:inline-flex;align-items:center;gap:6px;background:#ff9d00;color:#000;padding:8px 16px;border-radius:8px;font-size:13px;font-weight:600;text-decoration:none;"> |
| 🤗 Like on HuggingFace |
| </a> |
| </div> |
| <div id="repoStats" style="margin-top:14px;font-size:12px;color:#888;"></div> |
| </div> |
| </div> |
|
|
| <div class="input-bar"> |
| <div class="input-row"> |
| <input type="text" id="input" placeholder="Ask a question..." autocomplete="off" autofocus> |
| <button id="send" onclick="sendQuery()">Ask</button> |
| </div> |
| </div> |
|
|
| <div class="footer"> |
| <a href="https://github.com/tejasphatak/webmind-research">GitHub</a> |
| · <a href="https://huggingface.co/tejadabheja/guru">HuggingFace</a> |
| · No GPU required |
| </div> |
|
|
| <script> |
| const chat = document.getElementById('chat'); |
| const input = document.getElementById('input'); |
| const sendBtn = document.getElementById('send'); |
| let history = []; |
| let pendingTeachQuestion = null; |
| |
| input.addEventListener('keydown', e => { if (e.key === 'Enter' && !sendBtn.disabled) sendQuery(); }); |
| |
| function toggleStatus() { |
| const p = document.getElementById('statusPanel'); |
| p.classList.toggle('open'); |
| if (p.classList.contains('open')) loadStatus(); |
| } |
| |
| fetch('/health').then(r=>r.json()).then(s=>{ |
| document.getElementById('badge').textContent = |
| (s.words||0).toLocaleString() + ' neurons | ' + (s.rss_mb||0).toFixed(0) + ' MB'; |
| }).catch(()=>{ |
| document.getElementById('badge').textContent = 'Offline'; |
| }); |
| |
| function loadStatus() { |
| fetch('/health').then(r=>r.json()).then(s=>{ |
| document.getElementById('statusGrid').innerHTML = ` |
| <div class="s-card"><div class="s-label">Neurons</div><div class="s-val">${(s.words||0).toLocaleString()}</div></div> |
| <div class="s-card"><div class="s-label">Memory</div><div class="s-val">${(s.rss_mb||0).toFixed(0)} MB</div></div> |
| <div class="s-card"><div class="s-label">Disk Free</div><div class="s-val">${(s.disk_free_gb||0).toFixed(0)} GB</div></div> |
| <div class="s-card"><div class="s-label">Status</div><div class="s-val" style="color:#2d8a4e">${s.death_risk===0?'Alive':'Degraded'}</div></div> |
| <div class="s-card"><div class="s-label">Arch</div><div class="s-val">x86_64</div><div class="s-detail">CPU only</div></div> |
| <div class="s-card"><div class="s-label">Engine</div><div class="s-val">Graph</div><div class="s-detail">Convergence loop</div></div> |
| `; |
| }); |
| } |
| |
| function addMsg(role, html, meta) { |
| const w = chat.querySelector('.welcome'); if (w) w.remove(); |
| const d = document.createElement('div'); d.className = 'msg ' + role; |
| const b = document.createElement('div'); b.className = 'bubble'; b.innerHTML = html; d.appendChild(b); |
| if (meta) { const m = document.createElement('div'); m.className = 'meta'; m.textContent = meta; d.appendChild(m); } |
| chat.appendChild(d); chat.scrollTop = chat.scrollHeight; |
| return b; |
| } |
| |
| async function sendQuery() { |
| const q = input.value.trim(); if (!q) return; |
| addMsg('user', esc(q)); |
| input.value = ''; sendBtn.disabled = true; |
| |
| |
| if (pendingTeachQuestion) { |
| try { |
| await fetch('/v1/correct', { |
| method:'POST', headers:{'Content-Type':'application/json'}, |
| body: JSON.stringify({question: pendingTeachQuestion, answer: q}) |
| }); |
| const lb = addMsg('bot', 'Thank you! I learned that. Try asking me again.'); |
| const m = document.createElement('div'); m.className='meta'; m.textContent='taught'; |
| lb.parentElement.appendChild(m); |
| history.push({role:'user', content:q}); |
| history.push({role:'assistant', content:'Thank you! I learned that.'}); |
| } catch(e) { addMsg('bot', 'Error learning: '+e.message); } |
| pendingTeachQuestion = null; |
| sendBtn.disabled = false; input.focus(); |
| return; |
| } |
| |
| history.push({role:'user', content:q}); |
| const lb = addMsg('bot', '<span class="dots">Thinking</span>'); |
| const t0 = performance.now(); |
| try { |
| const r = await fetch('/v1/chat/completions', { |
| method:'POST', headers:{'Content-Type':'application/json'}, |
| body: JSON.stringify({model:'guru', messages:history, max_tokens:60}) |
| }); |
| const j = await r.json(); |
| const a = j.choices?.[0]?.message?.content || "I don't know."; |
| const guru = j.guru || {}; |
| const ms = Math.round(performance.now()-t0); |
| history.push({role:'assistant', content:a}); |
| lb.innerHTML = esc(a); |
| |
| |
| let metaText = ms + 'ms'; |
| if (guru.source === 'web') metaText += ' | searched the web'; |
| else if (guru.source === 'brain') metaText += ' | from knowledge graph'; |
| else if (guru.source === 'compute') metaText += ' | computed'; |
| if (guru.hops > 0) metaText += ' | ' + guru.hops + ' hops'; |
| if (guru.strategy === 'qa_direct') metaText += ' | direct match'; |
| const m = document.createElement('div'); m.className='meta'; m.textContent=metaText; |
| lb.parentElement.appendChild(m); |
| |
| |
| if (a.includes("teach me") || a.includes("Can you teach")) { |
| pendingTeachQuestion = q; |
| input.placeholder = "Type the answer to teach Guru..."; |
| } else { |
| input.placeholder = "Ask a question..."; |
| } |
| } catch(e) { lb.innerHTML = 'Error: '+e.message; } |
| sendBtn.disabled = false; input.focus(); chat.scrollTop = chat.scrollHeight; |
| } |
| |
| function esc(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); } |
| |
| |
| (async function() { |
| const el = document.getElementById('repoStats'); |
| if (!el) return; |
| let parts = []; |
| try { |
| const gh = await fetch('https://api.github.com/repos/tejasphatak/webmind-research').then(r=>r.json()); |
| if (gh.stargazers_count !== undefined) parts.push('GitHub: ' + gh.stargazers_count + ' stars, ' + gh.forks_count + ' forks'); |
| } catch(e) {} |
| try { |
| const hf = await fetch('https://huggingface.co/api/models/tejadabheja/guru').then(r=>r.json()); |
| if (hf.likes !== undefined) parts.push('HuggingFace: ' + hf.likes + ' likes, ' + hf.downloads + ' downloads'); |
| } catch(e) {} |
| if (parts.length) el.textContent = parts.join(' | '); |
| })(); |
| </script> |
| </body> |
| </html> |
|
|