Spaces:
Running
Running
File size: 4,930 Bytes
6fbbbb9 afd8fb2 6fbbbb9 afd8fb2 cedc3d6 afd8fb2 cedc3d6 afd8fb2 cedc3d6 afd8fb2 cedc3d6 afd8fb2 d190577 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | <!DOCTYPE html>
<html lang="en" class="h-full bg-gray-950 text-gray-100">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RAG Visualizer • Browser RAG</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2"></script>
<!-- Add other CDNs if needed: e.g., for charts or flow -->
<style>
/* Custom styles for highlights, nodes, etc. */
.vector-row { transition: all 0.3s; }
.highlight { animation: pulse 1.5s; background-color: #4f46e5; }
@keyframes pulse { 0%,100% {opacity:1} 50% {opacity:0.7} }
.node { border: 2px solid #6366f1; }
</style>
</head>
<body class="h-screen flex flex-col overflow-hidden">
<header class="bg-gray-900 p-4 border-b border-gray-700 flex justify-between">
<h1 class="text-2xl font-bold">Browser RAG Visualizer</h1>
<div id="status" class="text-sm text-green-400">Models loading...</div>
</header>
<div class="flex flex-1 overflow-hidden">
<!-- Left: Chat -->
<div class="w-1/3 border-r border-gray-700 flex flex-col">
<div id="chat" class="flex-1 p-4 overflow-y-auto space-y-4"></div>
<div class="p-4 border-t border-gray-700">
<input id="chatInput" type="text" class="w-full bg-gray-800 p-3 rounded" placeholder="Ask a question...">
</div>
</div>
<!-- Main Area -->
<div class="flex-1 flex flex-col">
<!-- Node Flow -->
<div class="h-1/3 border-b border-gray-700 p-4 overflow-auto" id="flow">
<!-- Simplified nodes: draggable divs or SVG -->
<div class="flex gap-4">
<div class="node p-4 rounded bg-gray-800 min-w-32">Embed</div>
<div class="node p-4 rounded bg-gray-800 min-w-32">Store (VectorDB)</div>
<div class="node p-4 rounded bg-gray-800 min-w-32">Retrieve Top-K</div>
<div class="node p-4 rounded bg-gray-800 min-w-32">Rerank</div>
<div class="node p-4 rounded bg-gray-800 min-w-32">Generate</div>
</div>
</div>
<!-- Vector Table & Controls -->
<div class="flex-1 p-4 overflow-auto">
<h2 class="text-lg mb-2">Vector Database</h2>
<button onclick="addEntry()" class="bg-indigo-600 px-4 py-2 rounded mb-4">Add Entry</button>
<table class="w-full" id="vectorTable">
<thead><tr><th>Text</th><th>Metadata</th><th>Date</th></tr></thead>
<tbody></tbody>
</table>
</div>
</div>
<!-- Right: Details -->
<div class="w-1/4 border-l border-gray-700 p-4 overflow-y-auto">
<h2>Top-K / Context</h2>
<div id="topk"></div>
<h2 class="mt-6">Reranking</h2>
<div id="rerank"></div>
</div>
</div>
<script>
// Global state
let vectors = []; // {id, text, embedding, metadata, date}
let embedder, generator, reranker;
async function initModels() {
const status = document.getElementById('status');
status.textContent = 'Loading embedding model...';
embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', { device: 'webgpu' }); // or 'wasm'
status.textContent = 'Loading LLM...';
// generator = await pipeline('text-generation', 'Xenova/Qwen2.5-0.5B-Instruct', { device: 'webgpu', dtype: 'q4' });
status.textContent = 'Ready!';
}
async function getEmbedding(text) {
const output = await embedder(text, { pooling: 'mean', normalize: true });
return Array.from(output.data);
}
async function addEntry() {
const text = prompt("Enter text to add:");
if (!text) return;
const emb = await getEmbedding(text);
vectors.push({
id: Date.now(),
text,
embedding: emb,
metadata: { source: "user" },
date: new Date().toISOString()
});
renderTable();
// Animate node
}
function cosineSimilarity(a, b) {
// Simple implementation
let dot = 0, magA = 0, magB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
magA += a[i] ** 2;
magB += b[i] ** 2;
}
return dot / (Math.sqrt(magA) * Math.sqrt(magB));
}
async function search(query, k=5) {
const qEmb = await getEmbedding(query);
const scored = vectors.map(v => ({
...v,
score: cosineSimilarity(qEmb, v.embedding)
})).sort((a,b) => b.score - a.score).slice(0,k);
// Highlight in table + show topk
renderTopK(scored);
// Trigger rerank, etc.
return scored;
}
function renderTable() {
// Populate tbody with rows, add click handlers
}
// Chat handler: on submit -> search -> (rerank) -> context -> generate -> append to chat
// Init
window.onload = () => {
initModels();
// Tailwind script already loaded
};
</script>
</body>
</html> |