RAG-Visualizer / index.html
quickgrid's picture
Update index.html
4ecad3c verified
raw
history blame
11.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RAG Visualizer (HF Spaces)</title>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.11.0/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@2.10.0/dist/transformers.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lancedb@0.5.0/dist/lance-wasm.min.js"></script>
<style>
:root {
--primary: #3b82f6;
--secondary: #1e40af;
--bg: #f8fafc;
--text: #0f172a;
--border: #e2e8f0;
}
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', sans-serif; }
body {
background: var(--bg);
color: var(--text);
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr auto;
height: 100vh;
gap: 1rem;
padding: 1rem;
}
.section {
background: white;
border: 1px solid var(--border);
border-radius: 0.5rem;
padding: 1rem;
overflow: auto;
}
.chat-window {
display: flex;
flex-direction: column;
gap: 1rem;
height: 100%;
}
.chat-messages {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.message { padding: 0.5rem; border-radius: 0.25rem; background: var(--border); }
.message.user { background: var(--primary); color: white; }
.input-area { display: flex; gap: 0.5rem; }
input, button { padding: 0.5rem; border: 1px solid var(--border); border-radius: 0.25rem; }
button { background: var(--primary); color: white; cursor: pointer; }
button:hover { background: var(--secondary); }
#vector-table { width: 100%; border-collapse: collapse; }
#vector-table th, #vector-table td { border: 1px solid var(--border); padding: 0.5rem; text-align: left; }
#vector-table tr.highlight { background: #fef3c7; animation: pulse 1s; }
@keyframes pulse { 0% { background: #fef3c7; } 50% { background: #fde68a; } 100% { background: #fef3c7; } }
#node-editor { height: 300px; border: 1px solid var(--border); border-radius: 0.25rem; }
.tab { padding: 0.5rem 1rem; background: var(--border); cursor: pointer; }
.tab.active { background: var(--primary); color: white; }
.tabs { display: flex; gap: 0.25rem; margin-bottom: 1rem; }
.tab-content { display: none; }
.tab-content.active { display: block; }
</style>
</head>
<body>
<!-- Left: Chat Section -->
<div class="section">
<h2>Chat with RAG</h2>
<div class="chat-window">
<div class="chat-messages" id="chat-messages"></div>
<div class="input-area">
<input type="text" id="user-input" placeholder="Ask a question..." />
<button onclick="sendMessage()">Send</button>
</div>
</div>
</div>
<!-- Right: Vector DB + Node Editor -->
<div class="section">
<div class="tabs">
<div class="tab active" onclick="switchTab('vector-db')">Vector DB</div>
<div class="tab" onclick="switchTab('node-editor')">Node Flow</div>
<div class="tab" onclick="switchTab('reranker')">Reranker</div>
</div>
<div id="vector-db" class="tab-content active">
<h2>Vector DB Entries</h2>
<div class="input-area">
<input type="text" id="db-input" placeholder="Add text to vector DB..." />
<button onclick="addToVectorDB()">Add</button>
</div>
<table id="vector-table">
<thead>
<tr>
<th>Text</th>
<th>Metadata</th>
<th>Date</th>
<th>Score</th>
</tr>
</thead>
<tbody id="vector-entries"></tbody>
</table>
</div>
<div id="node-editor" class="tab-content">
<h2>Node Flow Editor</h2>
<div id="node-editor-container" style="height: 100%;"></div>
</div>
<div id="reranker" class="tab-content">
<h2>Reranker</h2>
<div id="reranker-results"></div>
</div>
</div>
<script>
// --- State ---
let db;
let pipeline;
let currentTab = 'vector-db';
let vectorEntries = [];
// --- Init ---
async function init() {
// 1. Load Transformers.js pipeline for Qwen3.5-0.8B (quantized)
console.log("Loading Qwen3.5-0.8B (4-bit quantized)...");
pipeline = await transformers.pipeline(
'text-generation',
'Qwen/Qwen3.5-0.8B-4bit',
{ device: 'webgpu' } // or 'webnn'/'cpu'
);
console.log("Qwen3.5-0.8B loaded!");
// 2. Initialize LanceDB (WASM)
console.log("Initializing LanceDB (WASM)...");
await lanceDb.init();
db = await lanceDb.connect("/lancedb"); // Uses IndexedDB
const table = await db.createTable("vectors", [
{ vector: [], text: "", metadata: {}, date: new Date().toISOString() }
]);
console.log("LanceDB ready!");
// 3. Load existing entries (if any)
const existing = await table.search().limit(100).execute();
vectorEntries = existing;
renderVectorTable();
// 4. Init Node Editor (placeholder)
initNodeEditor();
}
// --- Chat ---
function sendMessage() {
const input = document.getElementById("user-input");
const message = input.value.trim();
if (!message) return;
addMessage("user", message);
input.value = "";
// Simulate RAG workflow
setTimeout(() => {
// 1. Embed query (placeholder: use actual embedding model)
const queryEmbedding = [0.1, 0.2, 0.3]; // Replace with real embedding
// 2. Search LanceDB
searchVectorDB(queryEmbedding).then(results => {
// 3. Rerank (placeholder)
const reranked = rerankResults(results);
addMessage("assistant", `Answer: ${generateResponse(reranked)}`);
// 4. Highlight top-K in table
highlightEntries(reranked.slice(0, 3).map(r => r.id));
});
}, 500);
}
function addMessage(sender, text) {
const chat = document.getElementById("chat-messages");
const msg = document.createElement("div");
msg.className = `message ${sender}`;
msg.textContent = text;
chat.appendChild(msg);
chat.scrollTop = chat.scrollHeight;
}
// --- Vector DB ---
async function addToVectorDB() {
const input = document.getElementById("db-input");
const text = input.value.trim();
if (!text) return;
input.value = "";
// 1. Embed text (placeholder)
const embedding = [0.4, 0.5, 0.6]; // Replace with real embedding model
// 2. Add to LanceDB
const table = await db.openTable("vectors");
const entry = {
vector: embedding,
text: text,
metadata: { source: "user" },
date: new Date().toISOString()
};
await table.add([entry]);
vectorEntries.push(entry);
renderVectorTable();
}
async function searchVectorDB(queryEmbedding, k = 3) {
const table = await db.openTable("vectors");
const results = await table
.search(queryEmbedding)
.limit(k)
.execute();
return results;
}
function renderVectorTable() {
const tbody = document.getElementById("vector-entries");
tbody.innerHTML = "";
vectorEntries.forEach((entry, i) => {
const row = document.createElement("tr");
row.innerHTML = `
<td>${entry.text.substring(0, 50)}...</td>
<td>${JSON.stringify(entry.metadata)}</td>
<td>${new Date(entry.date).toLocaleString()}</td>
<td>${entry.score || "N/A"}</td>
`;
row.id = `entry-${i}`;
tbody.appendChild(row);
});
}
function highlightEntries(ids) {
document.querySelectorAll("#vector-entries tr").forEach((row, i) => {
row.classList.toggle("highlight", ids.includes(i));
});
}
// --- Reranker (Placeholder) ---
function rerankResults(results) {
// Replace with actual reranker model
return results.map((r, i) => ({ ...r, score: 1 - (i * 0.1) }));
}
// --- Node Editor (Placeholder) ---
function initNodeEditor() {
const container = document.getElementById("node-editor-container");
container.innerHTML = `
<div style="padding: 1rem; background: #f1f5f9; border-radius: 0.25rem;">
<p>Node flow editor will go here. Use <a href="https://xyflow.com/" target="_blank">xyflow</a> or a custom SVG-based solution.</p>
<p>Example nodes:</p>
<ul>
<li>🔹 Embedding Model (BAAI/bge-small-en-v1.5)</li>
<li>📊 Vector DB (LanceDB WASM)</li>
<li>🔄 Reranker (BAAI/bge-reranker-base)</li>
<li>💬 LLM (Qwen3.5-0.8B)</li>
</ul>
</div>
`;
}
// --- Generation (Placeholder) ---
async function generateResponse(context) {
// Replace with actual Qwen3.5-0.8B generation
const prompt = `Context: ${context.map(c => c.text).join("\n")}\n\nAnswer:`;
const output = await pipeline(prompt, {
max_new_tokens: 200,
temperature: 0.7,
});
return output[0].generated_text;
}
// --- Tabs ---
function switchTab(tabName) {
document.querySelectorAll(".tab").forEach(t => t.classList.remove("active"));
document.querySelectorAll(".tab-content").forEach(c => c.classList.remove("active"));
document.getElementById(tabName).classList.add("active");
event.target.classList.add("active");
currentTab = tabName;
}
// --- Start App ---
init().catch(console.error);
</script>
</body>
</html>