ornith / index.html
devarshia5's picture
Upload 7 files
ed7c75c verified
Raw
History Blame Contribute Delete
4.83 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CPU RAG · Qwen3.5-0.8B + FAISS</title>
<style>
:root { color-scheme: light dark; --bg:#0f1220; --panel:#1a1e33; --acc:#7c8cff; --mut:#8b90a8; }
* { box-sizing: border-box; }
body { margin:0; font-family: system-ui, sans-serif; background:var(--bg); color:#e7e9f3; }
header { padding:14px 18px; border-bottom:1px solid #2a2f4a; display:flex; gap:12px; align-items:center; }
header h1 { font-size:15px; margin:0; font-weight:600; }
header .tag { font-size:11px; color:var(--mut); background:var(--panel); padding:3px 8px; border-radius:20px; }
main { max-width:820px; margin:0 auto; padding:18px; }
#chat { display:flex; flex-direction:column; gap:12px; min-height:50vh; }
.msg { padding:11px 14px; border-radius:12px; max-width:88%; white-space:pre-wrap; line-height:1.45; font-size:14px; }
.user { align-self:flex-end; background:var(--acc); color:#fff; }
.bot { align-self:flex-start; background:var(--panel); }
.src { align-self:flex-start; font-size:11px; color:var(--mut); margin-top:-6px; }
form { display:flex; gap:8px; margin-top:16px; }
textarea { flex:1; resize:none; padding:11px; border-radius:10px; border:1px solid #2a2f4a; background:#12152a; color:#e7e9f3; font-size:14px; }
button { padding:0 16px; border:0; border-radius:10px; background:var(--acc); color:#fff; font-weight:600; cursor:pointer; }
button:disabled { opacity:.5; cursor:default; }
.bar { display:flex; gap:10px; align-items:center; margin-bottom:14px; font-size:12px; color:var(--mut); }
.bar input[type=file] { font-size:12px; color:var(--mut); }
</style>
</head>
<body>
<header>
<h1>🦅 CPU RAG</h1>
<span class="tag">Qwen3.5-0.8B · bge-small · FAISS</span>
<span class="tag" id="stat"></span>
</header>
<main>
<div class="bar">
<label>Add a document (.txt/.md):</label>
<input type="file" id="file" accept=".txt,.md" />
<button id="up" type="button">Upload</button>
<span id="upmsg"></span>
</div>
<div id="chat"></div>
<form id="form">
<textarea id="q" rows="2" placeholder="Ask about your documents…"></textarea>
<button id="send" type="submit">Send</button>
</form>
</main>
<script>
const chat = document.getElementById('chat');
const stat = document.getElementById('stat');
function add(cls, text) {
const d = document.createElement('div');
d.className = 'msg ' + cls; d.textContent = text;
chat.appendChild(d); chat.scrollIntoView({block:'end'}); return d;
}
async function refreshStats(){
try { const s = await (await fetch('/stats')).json();
stat.textContent = s.indexed_chunks + ' chunks indexed'; } catch(e){}
}
refreshStats();
document.getElementById('up').onclick = async () => {
const f = document.getElementById('file').files[0];
if (!f) return;
document.getElementById('upmsg').textContent = 'uploading…';
const fd = new FormData(); fd.append('file', f);
const r = await (await fetch('/ingest', {method:'POST', body:fd})).json();
document.getElementById('upmsg').textContent = '+' + r.added_chunks + ' chunks';
refreshStats();
};
document.getElementById('form').onsubmit = async (e) => {
e.preventDefault();
const q = document.getElementById('q').value.trim();
if (!q) return;
document.getElementById('q').value = '';
document.getElementById('send').disabled = true;
add('user', q);
const thinking = add('bot', '…');
try {
// Stream tokens so the first word shows in ~1s instead of waiting for the
// whole answer (decode is ~5-6 tok/s on 2 vCPU).
const r = await fetch('/v1/chat/completions', {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ messages:[{role:'user', content:q}], temperature:0.3, stream:true })
});
const reader = r.body.getReader();
const dec = new TextDecoder();
let buf = '', out = '';
while (true) {
const {value, done} = await reader.read();
if (done) break;
buf += dec.decode(value, {stream:true});
const lines = buf.split('\n');
buf = lines.pop(); // keep partial line
for (const line of lines) {
const s = line.trim();
if (!s.startsWith('data:')) continue;
const payload = s.slice(5).trim();
if (payload === '[DONE]') continue;
try {
const tok = JSON.parse(payload).choices?.[0]?.delta?.content;
if (tok) { out += tok; thinking.textContent = out; chat.scrollIntoView({block:'end'}); }
} catch (_) {}
}
}
if (!out) thinking.textContent = '(no answer)';
} catch (err) {
thinking.textContent = 'Error: ' + err;
}
document.getElementById('send').disabled = false;
};
</script>
</body>
</html>