favicon + unified UI + status page
Browse files- chat.html +234 -0
- favicon.svg +37 -0
- server.py +101 -0
chat.html
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Guru — Self-Evolving AI</title>
|
| 7 |
+
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
| 8 |
+
<style>
|
| 9 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 10 |
+
body {
|
| 11 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 12 |
+
background: #f0faf0;
|
| 13 |
+
color: #1a1a1a;
|
| 14 |
+
min-height: 100vh;
|
| 15 |
+
display: flex;
|
| 16 |
+
flex-direction: column;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
/* Disclaimer */
|
| 20 |
+
.disclaimer {
|
| 21 |
+
background: #d32f2f; color: #fff;
|
| 22 |
+
padding: 8px 16px; font-size: 13px; text-align: center; line-height: 1.4;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/* Header */
|
| 26 |
+
.header {
|
| 27 |
+
background: #fff; border-bottom: 1px solid #c8e6c9;
|
| 28 |
+
padding: 12px 16px;
|
| 29 |
+
display: flex; align-items: center; justify-content: space-between;
|
| 30 |
+
flex-wrap: wrap; gap: 8px;
|
| 31 |
+
}
|
| 32 |
+
.brand { display: flex; align-items: center; gap: 10px; }
|
| 33 |
+
.brand-icon {
|
| 34 |
+
width: 36px; height: 36px; background: #2d8a4e; border-radius: 10px;
|
| 35 |
+
display: flex; align-items: center; justify-content: center;
|
| 36 |
+
color: #fff; font-weight: 700; font-size: 18px;
|
| 37 |
+
}
|
| 38 |
+
.brand-name { font-size: 20px; font-weight: 700; color: #1b5e20; }
|
| 39 |
+
.brand-sub { font-size: 11px; color: #666; }
|
| 40 |
+
.brand-sub a { color: #2d8a4e; text-decoration: none; }
|
| 41 |
+
.badge {
|
| 42 |
+
background: #e8f5e9; color: #1b5e20;
|
| 43 |
+
padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 600;
|
| 44 |
+
}
|
| 45 |
+
.status-btn {
|
| 46 |
+
background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 8px;
|
| 47 |
+
padding: 5px 12px; font-size: 12px; color: #1b5e20; cursor: pointer; font-weight: 600;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
/* Status */
|
| 51 |
+
.status { display: none; background: #fff; border-bottom: 1px solid #c8e6c9; padding: 16px; }
|
| 52 |
+
.status.open { display: block; }
|
| 53 |
+
.status-grid {
|
| 54 |
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
| 55 |
+
gap: 10px; max-width: 700px; margin: 0 auto;
|
| 56 |
+
}
|
| 57 |
+
.s-card { background: #e8f5e9; border-radius: 8px; padding: 10px 14px; }
|
| 58 |
+
.s-label { font-size: 10px; color: #666; text-transform: uppercase; letter-spacing: 0.5px; }
|
| 59 |
+
.s-val { font-size: 16px; font-weight: 700; color: #1b5e20; margin-top: 2px; }
|
| 60 |
+
.s-detail { font-size: 10px; color: #888; }
|
| 61 |
+
|
| 62 |
+
/* Chat */
|
| 63 |
+
.chat { flex: 1; overflow-y: auto; padding: 16px; max-width: 700px; width: 100%; margin: 0 auto; }
|
| 64 |
+
.welcome { text-align: center; padding: 40px 16px; color: #666; }
|
| 65 |
+
.welcome h2 { color: #1b5e20; font-size: 22px; margin-bottom: 8px; }
|
| 66 |
+
.welcome p { font-size: 14px; line-height: 1.6; max-width: 440px; margin: 0 auto; }
|
| 67 |
+
|
| 68 |
+
.msg { margin-bottom: 12px; max-width: 85%; animation: fadeIn 0.25s ease; }
|
| 69 |
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; } }
|
| 70 |
+
.msg.user { margin-left: auto; }
|
| 71 |
+
.msg.bot { margin-right: auto; }
|
| 72 |
+
.bubble {
|
| 73 |
+
padding: 10px 14px; border-radius: 14px; font-size: 15px; line-height: 1.5; word-wrap: break-word;
|
| 74 |
+
}
|
| 75 |
+
.msg.user .bubble { background: #2d8a4e; color: #fff; border-bottom-right-radius: 4px; }
|
| 76 |
+
.msg.bot .bubble { background: #f1f8e9; color: #1a1a1a; border: 1px solid #c8e6c9; border-bottom-left-radius: 4px; }
|
| 77 |
+
.meta { font-size: 11px; color: #999; margin-top: 3px; padding: 0 4px; }
|
| 78 |
+
.msg.user .meta { text-align: right; }
|
| 79 |
+
|
| 80 |
+
/* Loading dots */
|
| 81 |
+
.dots::after { content: ''; animation: d 1.5s infinite; }
|
| 82 |
+
@keyframes d { 0%{content:'.'} 33%{content:'..'} 66%{content:'...'} }
|
| 83 |
+
|
| 84 |
+
/* Input */
|
| 85 |
+
.input-bar {
|
| 86 |
+
background: #fff; border-top: 1px solid #c8e6c9; padding: 12px 16px;
|
| 87 |
+
}
|
| 88 |
+
.input-row {
|
| 89 |
+
max-width: 700px; margin: 0 auto; display: flex; gap: 8px;
|
| 90 |
+
}
|
| 91 |
+
.input-row input {
|
| 92 |
+
flex: 1; padding: 12px 16px; border: 2px solid #c8e6c9; border-radius: 12px;
|
| 93 |
+
font-size: 15px; outline: none; background: #f0faf0;
|
| 94 |
+
}
|
| 95 |
+
.input-row input:focus { border-color: #2d8a4e; }
|
| 96 |
+
.input-row button {
|
| 97 |
+
padding: 12px 20px; background: #2d8a4e; color: #fff; border: none;
|
| 98 |
+
border-radius: 12px; font-size: 15px; font-weight: 600; cursor: pointer;
|
| 99 |
+
}
|
| 100 |
+
.input-row button:hover { background: #1b5e20; }
|
| 101 |
+
.input-row button:disabled { background: #aaa; cursor: not-allowed; }
|
| 102 |
+
|
| 103 |
+
/* Footer */
|
| 104 |
+
.footer {
|
| 105 |
+
text-align: center; padding: 8px; font-size: 11px; color: #888;
|
| 106 |
+
background: #fff; border-top: 1px solid #c8e6c9;
|
| 107 |
+
}
|
| 108 |
+
.footer a { color: #2d8a4e; text-decoration: none; }
|
| 109 |
+
|
| 110 |
+
/* Mobile */
|
| 111 |
+
@media (max-width: 480px) {
|
| 112 |
+
.msg { max-width: 92%; }
|
| 113 |
+
.status-grid { grid-template-columns: repeat(2, 1fr); }
|
| 114 |
+
.s-val { font-size: 14px; }
|
| 115 |
+
.bubble { font-size: 14px; padding: 9px 12px; }
|
| 116 |
+
.input-row input { padding: 10px 12px; font-size: 14px; }
|
| 117 |
+
.input-row button { padding: 10px 16px; font-size: 14px; }
|
| 118 |
+
}
|
| 119 |
+
</style>
|
| 120 |
+
</head>
|
| 121 |
+
<body>
|
| 122 |
+
|
| 123 |
+
<div class="disclaimer">
|
| 124 |
+
<strong>RESEARCH PREVIEW</strong> — Experimental system. Do not submit personal or sensitive information. All inputs may be stored. No warranties.
|
| 125 |
+
</div>
|
| 126 |
+
|
| 127 |
+
<div class="header">
|
| 128 |
+
<div class="brand">
|
| 129 |
+
<div class="brand-icon">G</div>
|
| 130 |
+
<div>
|
| 131 |
+
<div class="brand-name">Guru</div>
|
| 132 |
+
<div class="brand-sub">Self-Evolving AI · <a href="https://webmind.sh">webmind.sh</a></div>
|
| 133 |
+
</div>
|
| 134 |
+
</div>
|
| 135 |
+
<div style="display:flex;gap:8px;align-items:center;">
|
| 136 |
+
<span class="badge" id="badge">Loading...</span>
|
| 137 |
+
<button class="status-btn" onclick="toggleStatus()">Status</button>
|
| 138 |
+
</div>
|
| 139 |
+
</div>
|
| 140 |
+
|
| 141 |
+
<div class="status" id="statusPanel">
|
| 142 |
+
<div class="status-grid" id="statusGrid"></div>
|
| 143 |
+
</div>
|
| 144 |
+
|
| 145 |
+
<div class="chat" id="chat">
|
| 146 |
+
<div class="welcome">
|
| 147 |
+
<h2>Ask anything</h2>
|
| 148 |
+
<p>Self-evolving graph reasoning engine. No GPU. No gradient descent. Learns from every conversation.</p>
|
| 149 |
+
</div>
|
| 150 |
+
</div>
|
| 151 |
+
|
| 152 |
+
<div class="input-bar">
|
| 153 |
+
<div class="input-row">
|
| 154 |
+
<input type="text" id="input" placeholder="Ask a question..." autocomplete="off" autofocus>
|
| 155 |
+
<button id="send" onclick="sendQuery()">Ask</button>
|
| 156 |
+
</div>
|
| 157 |
+
</div>
|
| 158 |
+
|
| 159 |
+
<div class="footer">
|
| 160 |
+
<a href="https://github.com/tejasphatak/webmind-research">GitHub</a>
|
| 161 |
+
· <a href="https://huggingface.co/tejadabheja/guru">HuggingFace</a>
|
| 162 |
+
· No GPU required
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
<script>
|
| 166 |
+
const chat = document.getElementById('chat');
|
| 167 |
+
const input = document.getElementById('input');
|
| 168 |
+
const sendBtn = document.getElementById('send');
|
| 169 |
+
let history = [];
|
| 170 |
+
|
| 171 |
+
input.addEventListener('keydown', e => { if (e.key === 'Enter' && !sendBtn.disabled) sendQuery(); });
|
| 172 |
+
|
| 173 |
+
function toggleStatus() {
|
| 174 |
+
const p = document.getElementById('statusPanel');
|
| 175 |
+
p.classList.toggle('open');
|
| 176 |
+
if (p.classList.contains('open')) loadStatus();
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
fetch('/health').then(r=>r.json()).then(s=>{
|
| 180 |
+
document.getElementById('badge').textContent =
|
| 181 |
+
(s.words||0).toLocaleString() + ' neurons | ' + (s.rss_mb||0).toFixed(0) + ' MB';
|
| 182 |
+
}).catch(()=>{
|
| 183 |
+
document.getElementById('badge').textContent = 'Offline';
|
| 184 |
+
});
|
| 185 |
+
|
| 186 |
+
function loadStatus() {
|
| 187 |
+
fetch('/health').then(r=>r.json()).then(s=>{
|
| 188 |
+
document.getElementById('statusGrid').innerHTML = `
|
| 189 |
+
<div class="s-card"><div class="s-label">Neurons</div><div class="s-val">${(s.words||0).toLocaleString()}</div></div>
|
| 190 |
+
<div class="s-card"><div class="s-label">Memory</div><div class="s-val">${(s.rss_mb||0).toFixed(0)} MB</div></div>
|
| 191 |
+
<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>
|
| 192 |
+
<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>
|
| 193 |
+
<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>
|
| 194 |
+
<div class="s-card"><div class="s-label">Engine</div><div class="s-val">Graph</div><div class="s-detail">Convergence loop</div></div>
|
| 195 |
+
`;
|
| 196 |
+
});
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
function addMsg(role, html, meta) {
|
| 200 |
+
const w = chat.querySelector('.welcome'); if (w) w.remove();
|
| 201 |
+
const d = document.createElement('div'); d.className = 'msg ' + role;
|
| 202 |
+
const b = document.createElement('div'); b.className = 'bubble'; b.innerHTML = html; d.appendChild(b);
|
| 203 |
+
if (meta) { const m = document.createElement('div'); m.className = 'meta'; m.textContent = meta; d.appendChild(m); }
|
| 204 |
+
chat.appendChild(d); chat.scrollTop = chat.scrollHeight;
|
| 205 |
+
return b;
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
async function sendQuery() {
|
| 209 |
+
const q = input.value.trim(); if (!q) return;
|
| 210 |
+
addMsg('user', esc(q));
|
| 211 |
+
input.value = ''; sendBtn.disabled = true;
|
| 212 |
+
history.push({role:'user', content:q});
|
| 213 |
+
const lb = addMsg('bot', '<span class="dots">Thinking</span>');
|
| 214 |
+
const t0 = performance.now();
|
| 215 |
+
try {
|
| 216 |
+
const r = await fetch('/v1/chat/completions', {
|
| 217 |
+
method:'POST', headers:{'Content-Type':'application/json'},
|
| 218 |
+
body: JSON.stringify({model:'guru', messages:history, max_tokens:60})
|
| 219 |
+
});
|
| 220 |
+
const j = await r.json();
|
| 221 |
+
const a = j.choices?.[0]?.message?.content || "I don't know.";
|
| 222 |
+
const ms = Math.round(performance.now()-t0);
|
| 223 |
+
history.push({role:'assistant', content:a});
|
| 224 |
+
lb.innerHTML = esc(a);
|
| 225 |
+
const m = document.createElement('div'); m.className='meta'; m.textContent=ms+'ms';
|
| 226 |
+
lb.parentElement.appendChild(m);
|
| 227 |
+
} catch(e) { lb.innerHTML = 'Error: '+e.message; }
|
| 228 |
+
sendBtn.disabled = false; input.focus(); chat.scrollTop = chat.scrollHeight;
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
function esc(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
| 232 |
+
</script>
|
| 233 |
+
</body>
|
| 234 |
+
</html>
|
favicon.svg
ADDED
|
|
server.py
CHANGED
|
@@ -67,8 +67,10 @@ if hasattr(brain, 'correct') and hasattr(brain, '_qa_map'):
|
|
| 67 |
app = FastAPI(title="Guru API", version="1.0.0")
|
| 68 |
|
| 69 |
from fastapi.responses import HTMLResponse, FileResponse
|
|
|
|
| 70 |
|
| 71 |
STATIC_DIR = os.path.join(os.path.dirname(__file__), 'static')
|
|
|
|
| 72 |
|
| 73 |
@app.get("/", response_class=HTMLResponse)
|
| 74 |
async def root():
|
|
@@ -77,6 +79,105 @@ async def root():
|
|
| 77 |
return FileResponse(chat_path)
|
| 78 |
return HTMLResponse("<h1>Guru API</h1><p>Use /v1/chat/completions or /health</p>")
|
| 79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
app.add_middleware(
|
| 81 |
CORSMiddleware,
|
| 82 |
allow_origins=["*"],
|
|
|
|
| 67 |
app = FastAPI(title="Guru API", version="1.0.0")
|
| 68 |
|
| 69 |
from fastapi.responses import HTMLResponse, FileResponse
|
| 70 |
+
from fastapi.staticfiles import StaticFiles
|
| 71 |
|
| 72 |
STATIC_DIR = os.path.join(os.path.dirname(__file__), 'static')
|
| 73 |
+
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
| 74 |
|
| 75 |
@app.get("/", response_class=HTMLResponse)
|
| 76 |
async def root():
|
|
|
|
| 79 |
return FileResponse(chat_path)
|
| 80 |
return HTMLResponse("<h1>Guru API</h1><p>Use /v1/chat/completions or /health</p>")
|
| 81 |
|
| 82 |
+
@app.get("/status", response_class=HTMLResponse)
|
| 83 |
+
async def status_page():
|
| 84 |
+
import platform, psutil, pathlib
|
| 85 |
+
h = brain.health()
|
| 86 |
+
proc = psutil.Process()
|
| 87 |
+
mem = proc.memory_info()
|
| 88 |
+
cpu_name = platform.processor() or "Unknown"
|
| 89 |
+
try:
|
| 90 |
+
with open("/proc/cpuinfo") as f:
|
| 91 |
+
for line in f:
|
| 92 |
+
if "model name" in line:
|
| 93 |
+
cpu_name = line.split(":")[1].strip()
|
| 94 |
+
break
|
| 95 |
+
except Exception:
|
| 96 |
+
pass
|
| 97 |
+
cores = os.cpu_count() or 0
|
| 98 |
+
total_ram = psutil.virtual_memory().total / (1024**3)
|
| 99 |
+
avail_ram = psutil.virtual_memory().available / (1024**3)
|
| 100 |
+
db_path = os.path.expanduser(DB_PATH)
|
| 101 |
+
lmdb_size = sum(f.stat().st_size for f in pathlib.Path(os.path.join(db_path, "brain.lmdb")).iterdir()) / (1024**3) if os.path.exists(os.path.join(db_path, "brain.lmdb")) else 0
|
| 102 |
+
csr_path = os.path.join(db_path, "cooc_csr")
|
| 103 |
+
csr_size = sum(f.stat().st_size for f in pathlib.Path(csr_path).iterdir()) / (1024**2) if os.path.exists(csr_path) else 0
|
| 104 |
+
|
| 105 |
+
html = f"""<!DOCTYPE html>
|
| 106 |
+
<html lang="en">
|
| 107 |
+
<head>
|
| 108 |
+
<meta charset="UTF-8">
|
| 109 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 110 |
+
<title>Guru — Status</title>
|
| 111 |
+
<style>
|
| 112 |
+
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
|
| 113 |
+
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace; background: #0d1117; color: #c9d1d9; padding: 2rem; }}
|
| 114 |
+
h1 {{ color: #58a6ff; margin-bottom: 0.5rem; font-size: 1.8rem; }}
|
| 115 |
+
.subtitle {{ color: #8b949e; margin-bottom: 2rem; }}
|
| 116 |
+
.grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; max-width: 900px; }}
|
| 117 |
+
.card {{ background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 1.5rem; }}
|
| 118 |
+
.card h2 {{ color: #58a6ff; font-size: 1rem; margin-bottom: 1rem; text-transform: uppercase; letter-spacing: 0.05em; }}
|
| 119 |
+
.row {{ display: flex; justify-content: space-between; padding: 0.4rem 0; border-bottom: 1px solid #21262d; }}
|
| 120 |
+
.row:last-child {{ border-bottom: none; }}
|
| 121 |
+
.label {{ color: #8b949e; }}
|
| 122 |
+
.value {{ color: #f0f6fc; font-weight: 600; }}
|
| 123 |
+
.green {{ color: #3fb950; }}
|
| 124 |
+
.yellow {{ color: #d29922; }}
|
| 125 |
+
.footer {{ margin-top: 2rem; color: #484f58; font-size: 0.85rem; }}
|
| 126 |
+
a {{ color: #58a6ff; text-decoration: none; }}
|
| 127 |
+
</style>
|
| 128 |
+
</head>
|
| 129 |
+
<body>
|
| 130 |
+
<div style="background:#da3633;color:#fff;padding:1rem 1.5rem;border-radius:8px;margin-bottom:1.5rem;font-size:0.95rem;max-width:900px;">
|
| 131 |
+
<strong>RESEARCH PREVIEW</strong> — This is an experimental system for research purposes only. Do not submit personal, confidential, or sensitive information. All inputs may be stored in the knowledge graph. No warranties. No SLA. May be taken offline without notice.
|
| 132 |
+
</div>
|
| 133 |
+
<h1>Guru</h1>
|
| 134 |
+
<p class="subtitle">Self-evolving graph reasoning engine — live on <a href="https://guru.webmind.sh">guru.webmind.sh</a></p>
|
| 135 |
+
<div class="grid">
|
| 136 |
+
<div class="card">
|
| 137 |
+
<h2>Infrastructure</h2>
|
| 138 |
+
<div class="row"><span class="label">CPU</span><span class="value">{cpu_name}</span></div>
|
| 139 |
+
<div class="row"><span class="label">Cores</span><span class="value">{cores}</span></div>
|
| 140 |
+
<div class="row"><span class="label">Architecture</span><span class="value">{platform.machine()}</span></div>
|
| 141 |
+
<div class="row"><span class="label">RAM</span><span class="value">{total_ram:.1f} GB total / {avail_ram:.1f} GB free</span></div>
|
| 142 |
+
<div class="row"><span class="label">GPU</span><span class="value">None</span></div>
|
| 143 |
+
<div class="row"><span class="label">OS</span><span class="value">{platform.system()} {platform.release()[:20]}</span></div>
|
| 144 |
+
</div>
|
| 145 |
+
<div class="card">
|
| 146 |
+
<h2>Brain</h2>
|
| 147 |
+
<div class="row"><span class="label">Neurons</span><span class="value">{h.get('neuron_count', h.get('word_count', 0)):,}</span></div>
|
| 148 |
+
<div class="row"><span class="label">Words</span><span class="value">{len(brain._words):,}</span></div>
|
| 149 |
+
<div class="row"><span class="label">Edges</span><span class="value">~{getattr(brain, '_csr', None) and brain._csr.nnz or 0:,}</span></div>
|
| 150 |
+
<div class="row"><span class="label">Q&A pairs</span><span class="value">{len(brain._qa_map):,}</span></div>
|
| 151 |
+
<div class="row"><span class="label">Status</span><span class="value green">{"Alive" if h.get('death_risk', 0) == 0 else "Degraded"}</span></div>
|
| 152 |
+
</div>
|
| 153 |
+
<div class="card">
|
| 154 |
+
<h2>Process</h2>
|
| 155 |
+
<div class="row"><span class="label">RSS</span><span class="value">{mem.rss / (1024**2):.0f} MB</span></div>
|
| 156 |
+
<div class="row"><span class="label">CPU %</span><span class="value">{proc.cpu_percent():.1f}%</span></div>
|
| 157 |
+
<div class="row"><span class="label">LMDB</span><span class="value">{lmdb_size:.2f} GB</span></div>
|
| 158 |
+
<div class="row"><span class="label">CSR</span><span class="value">{csr_size:.0f} MB</span></div>
|
| 159 |
+
<div class="row"><span class="label">Disk free</span><span class="value">{h.get('disk_free_gb', 0):.0f} GB</span></div>
|
| 160 |
+
</div>
|
| 161 |
+
<div class="card">
|
| 162 |
+
<h2>Architecture</h2>
|
| 163 |
+
<div class="row"><span class="label">Model</span><span class="value">{MODEL_NAME}</span></div>
|
| 164 |
+
<div class="row"><span class="label">Engine</span><span class="value">Co-occurrence graph + convergence loop</span></div>
|
| 165 |
+
<div class="row"><span class="label">Tier 1</span><span class="value">Q&A direct lookup (<1ms)</span></div>
|
| 166 |
+
<div class="row"><span class="label">Tier 2</span><span class="value">Sparse convergence (~250ms)</span></div>
|
| 167 |
+
<div class="row"><span class="label">Learning</span><span class="value">Every API call trains the brain</span></div>
|
| 168 |
+
<div class="row"><span class="label">Training</span><span class="value">No GPU. No gradient descent.</span></div>
|
| 169 |
+
</div>
|
| 170 |
+
</div>
|
| 171 |
+
<p class="footer">
|
| 172 |
+
Guru by <a href="https://webmind.sh">Webmind Research</a> ·
|
| 173 |
+
<a href="https://huggingface.co/tejadabheja/guru">HuggingFace</a> ·
|
| 174 |
+
<a href="https://github.com/tejasphatak/webmind-research">GitHub</a> ·
|
| 175 |
+
<a href="/">Chat</a>
|
| 176 |
+
</p>
|
| 177 |
+
</body>
|
| 178 |
+
</html>"""
|
| 179 |
+
return HTMLResponse(html)
|
| 180 |
+
|
| 181 |
app.add_middleware(
|
| 182 |
CORSMiddleware,
|
| 183 |
allow_origins=["*"],
|