quickgrid commited on
Commit
afd8fb2
·
verified ·
1 Parent(s): 4ecad3c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +132 -278
index.html CHANGED
@@ -1,286 +1,140 @@
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>RAG Visualizer (HF Spaces)</title>
7
- <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.11.0/dist/tf.min.js"></script>
8
- <script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@2.10.0/dist/transformers.min.js"></script>
9
- <script src="https://cdn.jsdelivr.net/npm/lancedb@0.5.0/dist/lance-wasm.min.js"></script>
10
- <style>
11
- :root {
12
- --primary: #3b82f6;
13
- --secondary: #1e40af;
14
- --bg: #f8fafc;
15
- --text: #0f172a;
16
- --border: #e2e8f0;
17
- }
18
- * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', sans-serif; }
19
- body {
20
- background: var(--bg);
21
- color: var(--text);
22
- display: grid;
23
- grid-template-columns: 1fr 1fr;
24
- grid-template-rows: 1fr auto;
25
- height: 100vh;
26
- gap: 1rem;
27
- padding: 1rem;
28
- }
29
- .section {
30
- background: white;
31
- border: 1px solid var(--border);
32
- border-radius: 0.5rem;
33
- padding: 1rem;
34
- overflow: auto;
35
- }
36
- .chat-window {
37
- display: flex;
38
- flex-direction: column;
39
- gap: 1rem;
40
- height: 100%;
41
- }
42
- .chat-messages {
43
- flex: 1;
44
- overflow-y: auto;
45
- display: flex;
46
- flex-direction: column;
47
- gap: 0.5rem;
48
- }
49
- .message { padding: 0.5rem; border-radius: 0.25rem; background: var(--border); }
50
- .message.user { background: var(--primary); color: white; }
51
- .input-area { display: flex; gap: 0.5rem; }
52
- input, button { padding: 0.5rem; border: 1px solid var(--border); border-radius: 0.25rem; }
53
- button { background: var(--primary); color: white; cursor: pointer; }
54
- button:hover { background: var(--secondary); }
55
- #vector-table { width: 100%; border-collapse: collapse; }
56
- #vector-table th, #vector-table td { border: 1px solid var(--border); padding: 0.5rem; text-align: left; }
57
- #vector-table tr.highlight { background: #fef3c7; animation: pulse 1s; }
58
- @keyframes pulse { 0% { background: #fef3c7; } 50% { background: #fde68a; } 100% { background: #fef3c7; } }
59
- #node-editor { height: 300px; border: 1px solid var(--border); border-radius: 0.25rem; }
60
- .tab { padding: 0.5rem 1rem; background: var(--border); cursor: pointer; }
61
- .tab.active { background: var(--primary); color: white; }
62
- .tabs { display: flex; gap: 0.25rem; margin-bottom: 1rem; }
63
- .tab-content { display: none; }
64
- .tab-content.active { display: block; }
65
- </style>
66
  </head>
67
- <body>
68
- <!-- Left: Chat Section -->
69
- <div class="section">
70
- <h2>Chat with RAG</h2>
71
- <div class="chat-window">
72
- <div class="chat-messages" id="chat-messages"></div>
73
- <div class="input-area">
74
- <input type="text" id="user-input" placeholder="Ask a question..." />
75
- <button onclick="sendMessage()">Send</button>
76
- </div>
77
- </div>
 
 
78
  </div>
79
-
80
- <!-- Right: Vector DB + Node Editor -->
81
- <div class="section">
82
- <div class="tabs">
83
- <div class="tab active" onclick="switchTab('vector-db')">Vector DB</div>
84
- <div class="tab" onclick="switchTab('node-editor')">Node Flow</div>
85
- <div class="tab" onclick="switchTab('reranker')">Reranker</div>
86
- </div>
87
- <div id="vector-db" class="tab-content active">
88
- <h2>Vector DB Entries</h2>
89
- <div class="input-area">
90
- <input type="text" id="db-input" placeholder="Add text to vector DB..." />
91
- <button onclick="addToVectorDB()">Add</button>
92
- </div>
93
- <table id="vector-table">
94
- <thead>
95
- <tr>
96
- <th>Text</th>
97
- <th>Metadata</th>
98
- <th>Date</th>
99
- <th>Score</th>
100
- </tr>
101
- </thead>
102
- <tbody id="vector-entries"></tbody>
103
- </table>
104
- </div>
105
- <div id="node-editor" class="tab-content">
106
- <h2>Node Flow Editor</h2>
107
- <div id="node-editor-container" style="height: 100%;"></div>
108
- </div>
109
- <div id="reranker" class="tab-content">
110
- <h2>Reranker</h2>
111
- <div id="reranker-results"></div>
112
  </div>
 
 
 
 
 
 
 
 
 
 
 
113
  </div>
114
-
115
- <script>
116
- // --- State ---
117
- let db;
118
- let pipeline;
119
- let currentTab = 'vector-db';
120
- let vectorEntries = [];
121
-
122
- // --- Init ---
123
- async function init() {
124
- // 1. Load Transformers.js pipeline for Qwen3.5-0.8B (quantized)
125
- console.log("Loading Qwen3.5-0.8B (4-bit quantized)...");
126
- pipeline = await transformers.pipeline(
127
- 'text-generation',
128
- 'Qwen/Qwen3.5-0.8B-4bit',
129
- { device: 'webgpu' } // or 'webnn'/'cpu'
130
- );
131
- console.log("Qwen3.5-0.8B loaded!");
132
-
133
- // 2. Initialize LanceDB (WASM)
134
- console.log("Initializing LanceDB (WASM)...");
135
- await lanceDb.init();
136
- db = await lanceDb.connect("/lancedb"); // Uses IndexedDB
137
- const table = await db.createTable("vectors", [
138
- { vector: [], text: "", metadata: {}, date: new Date().toISOString() }
139
- ]);
140
- console.log("LanceDB ready!");
141
-
142
- // 3. Load existing entries (if any)
143
- const existing = await table.search().limit(100).execute();
144
- vectorEntries = existing;
145
- renderVectorTable();
146
-
147
- // 4. Init Node Editor (placeholder)
148
- initNodeEditor();
149
- }
150
-
151
- // --- Chat ---
152
- function sendMessage() {
153
- const input = document.getElementById("user-input");
154
- const message = input.value.trim();
155
- if (!message) return;
156
- addMessage("user", message);
157
- input.value = "";
158
-
159
- // Simulate RAG workflow
160
- setTimeout(() => {
161
- // 1. Embed query (placeholder: use actual embedding model)
162
- const queryEmbedding = [0.1, 0.2, 0.3]; // Replace with real embedding
163
-
164
- // 2. Search LanceDB
165
- searchVectorDB(queryEmbedding).then(results => {
166
- // 3. Rerank (placeholder)
167
- const reranked = rerankResults(results);
168
- addMessage("assistant", `Answer: ${generateResponse(reranked)}`);
169
-
170
- // 4. Highlight top-K in table
171
- highlightEntries(reranked.slice(0, 3).map(r => r.id));
172
- });
173
- }, 500);
174
- }
175
-
176
- function addMessage(sender, text) {
177
- const chat = document.getElementById("chat-messages");
178
- const msg = document.createElement("div");
179
- msg.className = `message ${sender}`;
180
- msg.textContent = text;
181
- chat.appendChild(msg);
182
- chat.scrollTop = chat.scrollHeight;
183
- }
184
-
185
- // --- Vector DB ---
186
- async function addToVectorDB() {
187
- const input = document.getElementById("db-input");
188
- const text = input.value.trim();
189
- if (!text) return;
190
- input.value = "";
191
-
192
- // 1. Embed text (placeholder)
193
- const embedding = [0.4, 0.5, 0.6]; // Replace with real embedding model
194
-
195
- // 2. Add to LanceDB
196
- const table = await db.openTable("vectors");
197
- const entry = {
198
- vector: embedding,
199
- text: text,
200
- metadata: { source: "user" },
201
- date: new Date().toISOString()
202
- };
203
- await table.add([entry]);
204
- vectorEntries.push(entry);
205
- renderVectorTable();
206
- }
207
-
208
- async function searchVectorDB(queryEmbedding, k = 3) {
209
- const table = await db.openTable("vectors");
210
- const results = await table
211
- .search(queryEmbedding)
212
- .limit(k)
213
- .execute();
214
- return results;
215
- }
216
-
217
- function renderVectorTable() {
218
- const tbody = document.getElementById("vector-entries");
219
- tbody.innerHTML = "";
220
- vectorEntries.forEach((entry, i) => {
221
- const row = document.createElement("tr");
222
- row.innerHTML = `
223
- <td>${entry.text.substring(0, 50)}...</td>
224
- <td>${JSON.stringify(entry.metadata)}</td>
225
- <td>${new Date(entry.date).toLocaleString()}</td>
226
- <td>${entry.score || "N/A"}</td>
227
- `;
228
- row.id = `entry-${i}`;
229
- tbody.appendChild(row);
230
- });
231
- }
232
-
233
- function highlightEntries(ids) {
234
- document.querySelectorAll("#vector-entries tr").forEach((row, i) => {
235
- row.classList.toggle("highlight", ids.includes(i));
236
- });
237
- }
238
-
239
- // --- Reranker (Placeholder) ---
240
- function rerankResults(results) {
241
- // Replace with actual reranker model
242
- return results.map((r, i) => ({ ...r, score: 1 - (i * 0.1) }));
243
- }
244
-
245
- // --- Node Editor (Placeholder) ---
246
- function initNodeEditor() {
247
- const container = document.getElementById("node-editor-container");
248
- container.innerHTML = `
249
- <div style="padding: 1rem; background: #f1f5f9; border-radius: 0.25rem;">
250
- <p>Node flow editor will go here. Use <a href="https://xyflow.com/" target="_blank">xyflow</a> or a custom SVG-based solution.</p>
251
- <p>Example nodes:</p>
252
- <ul>
253
- <li>🔹 Embedding Model (BAAI/bge-small-en-v1.5)</li>
254
- <li>📊 Vector DB (LanceDB WASM)</li>
255
- <li>🔄 Reranker (BAAI/bge-reranker-base)</li>
256
- <li>💬 LLM (Qwen3.5-0.8B)</li>
257
- </ul>
258
- </div>
259
- `;
260
- }
261
-
262
- // --- Generation (Placeholder) ---
263
- async function generateResponse(context) {
264
- // Replace with actual Qwen3.5-0.8B generation
265
- const prompt = `Context: ${context.map(c => c.text).join("\n")}\n\nAnswer:`;
266
- const output = await pipeline(prompt, {
267
- max_new_tokens: 200,
268
- temperature: 0.7,
269
- });
270
- return output[0].generated_text;
271
- }
272
-
273
- // --- Tabs ---
274
- function switchTab(tabName) {
275
- document.querySelectorAll(".tab").forEach(t => t.classList.remove("active"));
276
- document.querySelectorAll(".tab-content").forEach(c => c.classList.remove("active"));
277
- document.getElementById(tabName).classList.add("active");
278
- event.target.classList.add("active");
279
- currentTab = tabName;
280
- }
281
-
282
- // --- Start App ---
283
- init().catch(console.error);
284
- </script>
285
  </body>
286
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="en" class="h-full bg-gray-950 text-gray-100">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>RAG Visualizer Browser RAG</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2"></script>
9
+ <!-- Add other CDNs if needed: e.g., for charts or flow -->
10
+ <style>
11
+ /* Custom styles for highlights, nodes, etc. */
12
+ .vector-row { transition: all 0.3s; }
13
+ .highlight { animation: pulse 1.5s; background-color: #4f46e5; }
14
+ @keyframes pulse { 0%,100% {opacity:1} 50% {opacity:0.7} }
15
+ .node { border: 2px solid #6366f1; }
16
+ </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  </head>
18
+ <body class="h-screen flex flex-col overflow-hidden">
19
+ <header class="bg-gray-900 p-4 border-b border-gray-700 flex justify-between">
20
+ <h1 class="text-2xl font-bold">Browser RAG Visualizer</h1>
21
+ <div id="status" class="text-sm text-green-400">Models loading...</div>
22
+ </header>
23
+
24
+ <div class="flex flex-1 overflow-hidden">
25
+ <!-- Left: Chat -->
26
+ <div class="w-1/3 border-r border-gray-700 flex flex-col">
27
+ <div id="chat" class="flex-1 p-4 overflow-y-auto space-y-4"></div>
28
+ <div class="p-4 border-t border-gray-700">
29
+ <input id="chatInput" type="text" class="w-full bg-gray-800 p-3 rounded" placeholder="Ask a question...">
30
+ </div>
31
  </div>
32
+
33
+ <!-- Main Area -->
34
+ <div class="flex-1 flex flex-col">
35
+ <!-- Node Flow -->
36
+ <div class="h-1/3 border-b border-gray-700 p-4 overflow-auto" id="flow">
37
+ <!-- Simplified nodes: draggable divs or SVG -->
38
+ <div class="flex gap-4">
39
+ <div class="node p-4 rounded bg-gray-800 min-w-32">Embed</div>
40
+ <div class="node p-4 rounded bg-gray-800 min-w-32">Store (VectorDB)</div>
41
+ <div class="node p-4 rounded bg-gray-800 min-w-32">Retrieve Top-K</div>
42
+ <div class="node p-4 rounded bg-gray-800 min-w-32">Rerank</div>
43
+ <div class="node p-4 rounded bg-gray-800 min-w-32">Generate</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  </div>
45
+ </div>
46
+
47
+ <!-- Vector Table & Controls -->
48
+ <div class="flex-1 p-4 overflow-auto">
49
+ <h2 class="text-lg mb-2">Vector Database</h2>
50
+ <button onclick="addEntry()" class="bg-indigo-600 px-4 py-2 rounded mb-4">Add Entry</button>
51
+ <table class="w-full" id="vectorTable">
52
+ <thead><tr><th>Text</th><th>Metadata</th><th>Date</th></tr></thead>
53
+ <tbody></tbody>
54
+ </table>
55
+ </div>
56
  </div>
57
+
58
+ <!-- Right: Details -->
59
+ <div class="w-1/4 border-l border-gray-700 p-4 overflow-y-auto">
60
+ <h2>Top-K / Context</h2>
61
+ <div id="topk"></div>
62
+ <h2 class="mt-6">Reranking</h2>
63
+ <div id="rerank"></div>
64
+ </div>
65
+ </div>
66
+
67
+ <script>
68
+ // Global state
69
+ let vectors = []; // {id, text, embedding, metadata, date}
70
+ let embedder, generator, reranker;
71
+
72
+ async function initModels() {
73
+ const status = document.getElementById('status');
74
+ status.textContent = 'Loading embedding model...';
75
+ embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', { device: 'webgpu' }); // or 'wasm'
76
+
77
+ status.textContent = 'Loading LLM...';
78
+ // generator = await pipeline('text-generation', 'Xenova/Qwen2.5-0.5B-Instruct', { device: 'webgpu', dtype: 'q4' });
79
+
80
+ status.textContent = 'Ready!';
81
+ }
82
+
83
+ async function getEmbedding(text) {
84
+ const output = await embedder(text, { pooling: 'mean', normalize: true });
85
+ return Array.from(output.data);
86
+ }
87
+
88
+ async function addEntry() {
89
+ const text = prompt("Enter text to add:");
90
+ if (!text) return;
91
+ const emb = await getEmbedding(text);
92
+ vectors.push({
93
+ id: Date.now(),
94
+ text,
95
+ embedding: emb,
96
+ metadata: { source: "user" },
97
+ date: new Date().toISOString()
98
+ });
99
+ renderTable();
100
+ // Animate node
101
+ }
102
+
103
+ function cosineSimilarity(a, b) {
104
+ // Simple implementation
105
+ let dot = 0, magA = 0, magB = 0;
106
+ for (let i = 0; i < a.length; i++) {
107
+ dot += a[i] * b[i];
108
+ magA += a[i] ** 2;
109
+ magB += b[i] ** 2;
110
+ }
111
+ return dot / (Math.sqrt(magA) * Math.sqrt(magB));
112
+ }
113
+
114
+ async function search(query, k=5) {
115
+ const qEmb = await getEmbedding(query);
116
+ const scored = vectors.map(v => ({
117
+ ...v,
118
+ score: cosineSimilarity(qEmb, v.embedding)
119
+ })).sort((a,b) => b.score - a.score).slice(0,k);
120
+
121
+ // Highlight in table + show topk
122
+ renderTopK(scored);
123
+ // Trigger rerank, etc.
124
+ return scored;
125
+ }
126
+
127
+ function renderTable() {
128
+ // Populate tbody with rows, add click handlers
129
+ }
130
+
131
+ // Chat handler: on submit -> search -> (rerank) -> context -> generate -> append to chat
132
+
133
+ // Init
134
+ window.onload = () => {
135
+ initModels();
136
+ // Tailwind script already loaded
137
+ };
138
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  </body>
140
  </html>