Download index.html from FurqanAli12345/rag-document-assistant: direct link, hf CLI and curl.
- Browser
- Download file 6.97 kB
-
https://huggingface.co/spaces/FurqanAli12345/rag-document-assistant/resolve/main/index.html
- Command line
-
hf download hf://spaces/FurqanAli12345/rag-document-assistant/index.html
-
curl -L -o index.html https://huggingface.co/spaces/FurqanAli12345/rag-document-assistant/resolve/main/index.html
6.97 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>RAG Document Assistant</title> | |
| <style> | |
| :root{--bg:#f6f8fa;--card:#fff;--ink:#0f172a;--muted:#64748b;--line:#e2e8f0;--brand:#0f766e;--quote:#f1f5f9} | |
| @media(prefers-color-scheme:dark){:root{--bg:#0b1220;--card:#131c2b;--ink:#e6edf6;--muted:#94a3b8;--line:#243247;--brand:#5eead4;--quote:#0e1726}} | |
| *{box-sizing:border-box} | |
| body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;background:var(--bg);color:var(--ink);line-height:1.55} | |
| .wrap{max-width:980px;margin:0 auto;padding:28px 18px 60px} | |
| h1{font-size:1.7rem;margin:0 0 6px}.sub{color:var(--muted);margin:0 0 18px} | |
| .badges span{display:inline-block;font-size:.72rem;background:var(--card);border:1px solid var(--line);color:var(--muted);border-radius:999px;padding:3px 10px;margin:2px 4px 2px 0} | |
| .grid{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:16px} | |
| @media(max-width:760px){.grid{grid-template-columns:1fr}} | |
| .card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:18px} | |
| button.go{background:var(--brand);color:#04231f;border:none;border-radius:10px;padding:10px 16px;font-weight:700;cursor:pointer} | |
| button.ghost{background:var(--bg);color:var(--ink);border:1px solid var(--line);border-radius:10px;padding:10px 16px;font-weight:600;cursor:pointer} | |
| button:hover{filter:brightness(1.06)} | |
| input[type=text]{width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:var(--ink);font-size:.95rem} | |
| input[type=file]{color:var(--muted);font-size:.9rem;margin-bottom:10px;width:100%} | |
| .row{display:flex;gap:10px;flex-wrap:wrap;margin-top:6px} | |
| .note{color:var(--muted);font-size:.86rem;margin-top:10px} | |
| .answer{margin-top:14px}.answer h3{margin:0 0 6px;font-size:1rem} | |
| .src{margin-top:6px;border-left:3px solid var(--brand);padding:8px 12px;background:var(--quote);border-radius:0 8px 8px 0} | |
| .src .h{font-size:.8rem;color:var(--muted)}.src blockquote{margin:4px 0 0;color:var(--ink);font-size:.9rem} | |
| .chips button{font-size:.8rem;margin:4px 6px 0 0;background:var(--bg);border:1px solid var(--line);color:var(--ink);border-radius:8px;padding:5px 10px;cursor:pointer} | |
| details{margin-top:20px;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 18px} | |
| summary{cursor:pointer;font-weight:600}a{color:var(--brand)} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <h1>📄 RAG Document Assistant</h1> | |
| <p class="sub">Ask questions about your own documents — answers are grounded in your text, cited to the source, and it says “I don't know” instead of guessing.</p> | |
| <div class="badges"><span>overlapping chunking</span><span>TF-IDF embeddings</span><span>cosine retrieval</span><span>citations</span><span>honest refusal</span><span>100% in-browser</span></div> | |
| <div class="grid"> | |
| <div class="card"> | |
| <input type="file" id="files" accept=".txt,.md" multiple /> | |
| <div class="row"> | |
| <button class="go" id="buildBtn">Build index from uploads</button> | |
| <button class="ghost" id="sampleBtn">Load sample docs</button> | |
| </div> | |
| <div class="note" id="status">Upload .txt / .md files, or load the sample handbook to try it.</div> | |
| </div> | |
| <div class="card"> | |
| <input type="text" id="q" placeholder="e.g. What is the refund window?" /> | |
| <div class="row"><button class="go" id="askBtn">Ask</button></div> | |
| <div class="chips"> | |
| <button>What is the refund window?</button> | |
| <button>How do I reset my password?</button> | |
| <button>What are the support hours?</button> | |
| <button>Who is eligible for the loyalty program?</button> | |
| </div> | |
| <div class="answer" id="answer"></div> | |
| <div id="sources"></div> | |
| </div> | |
| </div> | |
| <details> | |
| <summary>ℹ️ How RAG works here, limits & data</summary> | |
| <p style="color:var(--muted)"><b>Chunking</b> splits documents into overlapping windows so context isn't cut at boundaries. <b>Embeddings</b> here use a TF-IDF vectoriser computed in your browser; <b>retrieval</b> ranks chunks by cosine similarity. Answers are built <b>only</b> from retrieved chunks and cited; below a similarity threshold the assistant returns <b>“I don't know”</b> rather than hallucinating. This static demo is <b>extractive</b> (returns the most relevant passage). The full Python version adds neural <code>sentence-transformers</code> embeddings and optional LLM generation. Uploaded files are processed in-memory and never leave your device. 🔗 <a href="https://github.com/furqunali/rag-document-assistant" target="_blank" rel="noopener">Source on GitHub</a></p> | |
| </details> | |
| </div> | |
| <script src="logic.js"></script> | |
| <script> | |
| const RAG=window.RAG,$=(id)=>document.getElementById(id); | |
| let retriever=null; | |
| function indexFrom(named){ | |
| retriever=RAG.buildRetriever(named); | |
| if(!retriever){$("status").textContent="No readable text found. Upload .txt or .md files with content.";return;} | |
| const chunks=retriever.chunks.length; | |
| $("status").innerHTML=`✅ Indexed <b>${chunks} chunks</b> from <b>${named.length} document(s)</b>. Embedder: <code>tfidf (in-browser)</code> · Mode: extractive (key-free).`; | |
| } | |
| $("sampleBtn").onclick=()=>indexFrom(RAG.SAMPLE_DOCS); | |
| $("buildBtn").onclick=()=>{ | |
| const fl=$("files").files; | |
| if(!fl.length){$("status").textContent="Choose one or more .txt/.md files first, or load the samples.";return;} | |
| let pending=fl.length,named=[]; | |
| [...fl].forEach(file=>{const rd=new FileReader(); | |
| rd.onload=()=>{if(String(rd.result).trim())named.push([file.name,String(rd.result)]); | |
| if(--pending===0)indexFrom(named);}; | |
| rd.readAsText(file);}); | |
| }; | |
| function ask(){ | |
| const q=$("q").value.trim(); | |
| if(!q){$("answer").innerHTML="Type a question about your documents.";return;} | |
| if(!retriever){$("answer").innerHTML="⚠️ Build an index first — upload docs or load the samples.";$("sources").innerHTML="";return;} | |
| const hits=retriever.query(q,4),res=RAG.answer(q,hits); | |
| const modeLine=res.grounded?`<div class="note">Mode: ${res.mode} · top similarity: ${res.top_score}</div>` | |
| :`<div class="note">(no confident match · top similarity: ${res.top_score})</div>`; | |
| $("answer").innerHTML=`<h3>Answer</h3><div>${res.answer}</div>${modeLine}`; | |
| $("sources").innerHTML=res.citations.length?("<h3 style='font-size:1rem;margin:14px 0 4px'>Sources</h3>"+ | |
| res.citations.map(c=>`<div class="src"><div class="h">${c.source} · chunk ${c.chunk} · sim ${c.score}</div><blockquote>${c.preview}</blockquote></div>`).join("")):""; | |
| } | |
| $("askBtn").onclick=ask; | |
| $("q").addEventListener("keydown",e=>{if(e.key==="Enter")ask();}); | |
| document.querySelectorAll(".chips button").forEach(b=>b.onclick=()=>{$("q").value=b.textContent;ask();}); | |
| </script> | |
| </body> | |
| </html> | |