ucalyptus commited on
Commit
15ad46e
·
verified ·
1 Parent(s): 0fbf422

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +435 -18
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Flashinfer Attention States Recursive Merge Operator Viz
3
- emoji: 👀
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: flashinfer-attention-states-recursive-merge-operator-viz
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,436 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
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>FlashInfer Attention State Visualization</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ body {
11
+ font-family: 'Inter', sans-serif;
12
+ background-color: #f3f4f6; /* Tailwind gray-100 */
13
+ }
14
+ canvas {
15
+ background-color: #ffffff; /* White */
16
+ border-radius: 0.5rem; /* rounded-lg */
17
+ box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); /* shadow-md */
18
+ }
19
+ .btn {
20
+ padding: 0.5rem 1rem; /* py-2 px-4 */
21
+ border-radius: 0.375rem; /* rounded-md */
22
+ font-weight: 600; /* font-semibold */
23
+ color: white;
24
+ background-color: #4f46e5; /* indigo-600 */
25
+ transition: background-color 0.2s;
26
+ cursor: pointer;
27
+ margin: 0 0.5rem; /* mx-2 */
28
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); /* shadow-sm */
29
+ }
30
+ .btn:hover {
31
+ background-color: #4338ca; /* indigo-700 */
32
+ }
33
+ .btn:disabled {
34
+ background-color: #a5b4fc; /* indigo-300 */
35
+ cursor: not-allowed;
36
+ }
37
+ .info-text {
38
+ color: #4b5563; /* gray-600 */
39
+ font-size: 0.875rem; /* text-sm */
40
+ margin-top: 0.5rem; /* mt-2 */
41
+ }
42
+ .state-box {
43
+ border: 2px solid;
44
+ border-radius: 0.375rem; /* rounded-md */
45
+ padding: 5px;
46
+ text-align: center;
47
+ font-size: 0.8rem;
48
+ background-color: rgba(255, 255, 255, 0.8);
49
+ }
50
+ .s-value {
51
+ font-weight: bold;
52
+ color: #1d4ed8; /* blue-700 */
53
+ }
54
+ .v-value {
55
+ display: inline-block;
56
+ width: 15px;
57
+ height: 15px;
58
+ border-radius: 50%;
59
+ margin-left: 5px;
60
+ vertical-align: middle;
61
+ }
62
+ </style>
63
+ </head>
64
+ <body class="flex flex-col items-center justify-center min-h-screen p-4">
65
+
66
+ <h1 class="text-2xl font-semibold text-gray-800 mb-4">FlashInfer: Attention States & Recursive Merge</h1>
67
+ <p class="text-center text-gray-600 mb-6 max-w-2xl">
68
+ This visualization demonstrates how FlashInfer computes attention by:
69
+ <br>1. Calculating partial "Attention States" (s, v) for subsets of Key-Value pairs.
70
+ <br>2. Recursively merging these states using the $\oplus$ operator to get the final result.
71
+ </p>
72
+
73
+ <canvas id="attentionCanvas" width="800" height="400"></canvas>
74
+ <p id="statusText" class="info-text h-6"></p> <div class="mt-6 flex justify-center">
75
+ <button id="resetBtn" class="btn">Reset & Initialize</button>
76
+ <button id="computeStatesBtn" class="btn" disabled>Compute Partial States</button>
77
+ <button id="mergeStatesBtn" class="btn" disabled>Merge States</button>
78
+ </div>
79
+
80
+ <script>
81
+ const canvas = document.getElementById('attentionCanvas');
82
+ const ctx = canvas.getContext('2d');
83
+ const statusText = document.getElementById('statusText');
84
+ const resetBtn = document.getElementById('resetBtn');
85
+ const computeStatesBtn = document.getElementById('computeStatesBtn');
86
+ const mergeStatesBtn = document.getElementById('mergeStatesBtn');
87
+
88
+ let animationFrameId = null;
89
+
90
+ // --- Configuration ---
91
+ const config = {
92
+ queryPos: { x: 50, y: 200 },
93
+ kvStartY: 50,
94
+ kvSpacingY: 60,
95
+ kvCount: 6, // Ensure this is even for easy partitioning
96
+ kvPartitionSize: 3, // Size of each partition
97
+ stateBoxWidth: 100,
98
+ stateBoxHeight: 50,
99
+ mergePointX: 600,
100
+ finalStateX: 700,
101
+ colors: {
102
+ query: '#ef4444', // red-500
103
+ kv: '#3b82f6', // blue-500
104
+ partition1: '#10b981', // emerald-500
105
+ partition2: '#f97316', // orange-500
106
+ merged: '#8b5cf6', // violet-500
107
+ arrow: '#6b7280', // gray-500
108
+ text: '#1f2937', // gray-800
109
+ },
110
+ arrowHeadSize: 8,
111
+ };
112
+
113
+ // --- State Variables ---
114
+ let query = {};
115
+ let kvPairs = [];
116
+ let states = {
117
+ partition1: null,
118
+ partition2: null,
119
+ final: null
120
+ };
121
+ let currentStep = 0; // 0: initial, 1: kvs shown, 2: states computed, 3: merged
122
+
123
+ // --- Drawing Functions ---
124
+ function drawArrow(startX, startY, endX, endY, color = config.colors.arrow, progress = 1) {
125
+ const dx = endX - startX;
126
+ const dy = endY - startY;
127
+ const length = Math.sqrt(dx * dx + dy * dy);
128
+ const angle = Math.atan2(dy, dx);
129
+
130
+ const currentX = startX + dx * progress;
131
+ const currentY = startY + dy * progress;
132
+
133
+ ctx.beginPath();
134
+ ctx.moveTo(startX, startY);
135
+ ctx.lineTo(currentX, currentY);
136
+ ctx.strokeStyle = color;
137
+ ctx.lineWidth = 1.5;
138
+ ctx.stroke();
139
+
140
+ if (progress >= 1) {
141
+ // Draw arrowhead
142
+ ctx.beginPath();
143
+ ctx.moveTo(currentX, currentY);
144
+ ctx.lineTo(currentX - config.arrowHeadSize * Math.cos(angle - Math.PI / 6), currentY - config.arrowHeadSize * Math.sin(angle - Math.PI / 6));
145
+ ctx.lineTo(currentX - config.arrowHeadSize * Math.cos(angle + Math.PI / 6), currentY - config.arrowHeadSize * Math.sin(angle + Math.PI / 6));
146
+ ctx.closePath();
147
+ ctx.fillStyle = color;
148
+ ctx.fill();
149
+ }
150
+ }
151
+
152
+ function drawQuery(q) {
153
+ ctx.fillStyle = config.colors.query;
154
+ ctx.fillRect(q.x - 15, q.y - 15, 30, 30);
155
+ ctx.fillStyle = config.colors.text;
156
+ ctx.font = 'bold 14px Inter';
157
+ ctx.textAlign = 'center';
158
+ ctx.fillText('Q', q.x, q.y + 5);
159
+ }
160
+
161
+ function drawKVPair(kv, index) {
162
+ const kvColor = config.colors.kv;
163
+ // Draw K
164
+ ctx.fillStyle = kvColor;
165
+ ctx.beginPath();
166
+ ctx.moveTo(kv.x - 10, kv.y - 10);
167
+ ctx.lineTo(kv.x, kv.y);
168
+ ctx.lineTo(kv.x - 10, kv.y + 10);
169
+ ctx.closePath();
170
+ ctx.fill();
171
+ // Draw V
172
+ ctx.beginPath();
173
+ ctx.arc(kv.x + 10, kv.y, 10, 0, Math.PI * 2);
174
+ ctx.fill();
175
+
176
+ ctx.fillStyle = config.colors.text;
177
+ ctx.font = '12px Inter';
178
+ ctx.textAlign = 'left';
179
+ ctx.fillText(`KV ${index + 1}`, kv.x + 25, kv.y + 4);
180
+ }
181
+
182
+ function drawAttentionState(state, color) {
183
+ if (!state) return;
184
+ ctx.strokeStyle = color;
185
+ ctx.lineWidth = 2;
186
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.9)'; // Semi-transparent white background
187
+ ctx.fillRect(state.x, state.y, config.stateBoxWidth, config.stateBoxHeight);
188
+ ctx.strokeRect(state.x, state.y, config.stateBoxWidth, config.stateBoxHeight);
189
+
190
+ ctx.fillStyle = config.colors.text;
191
+ ctx.font = 'bold 12px Inter';
192
+ ctx.textAlign = 'center';
193
+ ctx.fillText(state.label, state.x + config.stateBoxWidth / 2, state.y + 15);
194
+
195
+ ctx.font = '11px Inter';
196
+ // Draw 's' value (LSE)
197
+ ctx.fillStyle = config.colors.text; // Use standard text color for label
198
+ ctx.fillText('s:', state.x + 25, state.y + 35);
199
+ ctx.fillStyle = config.colors.text; // Use standard text color for value
200
+ ctx.textAlign = 'left';
201
+ ctx.fillText(state.s.toFixed(2), state.x + 35, state.y + 35); // Display LSE value
202
+
203
+ // Draw 'v' representation
204
+ ctx.fillStyle = config.colors.text; // Use standard text color for label
205
+ ctx.textAlign = 'center';
206
+ ctx.fillText('v:', state.x + 70, state.y + 35);
207
+ ctx.fillStyle = state.vColor; // Use the state's specific color for the 'v' circle
208
+ ctx.beginPath();
209
+ ctx.arc(state.x + 85, state.y + 30, 7, 0, Math.PI * 2); // Draw circle representing 'v'
210
+ ctx.fill();
211
+ }
212
+
213
+ // --- Animation Loop ---
214
+ let progress = 0;
215
+ const animationSpeed = 0.02;
216
+
217
+ function animateStep(stepFunction, nextStep) {
218
+ cancelAnimationFrame(animationFrameId); // Cancel previous animation if any
219
+ progress = 0;
220
+
221
+ function loop() {
222
+ progress += animationSpeed;
223
+ if (progress >= 1) {
224
+ progress = 1;
225
+ stepFunction(progress); // Draw final frame
226
+ currentStep = nextStep;
227
+ updateButtons();
228
+ setStatus(''); // Clear status after animation
229
+ return;
230
+ }
231
+
232
+ stepFunction(progress); // Draw intermediate frame
233
+ animationFrameId = requestAnimationFrame(loop);
234
+ }
235
+ loop();
236
+ }
237
+
238
+
239
+ // --- Visualization Steps ---
240
+ function initialize() {
241
+ cancelAnimationFrame(animationFrameId); // Stop any ongoing animation
242
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
243
+ currentStep = 0;
244
+ states = { partition1: null, partition2: null, final: null };
245
+
246
+ // Define Query
247
+ query = { ...config.queryPos };
248
+
249
+ // Define KV Pairs
250
+ kvPairs = [];
251
+ const kvStartX = config.queryPos.x + 100;
252
+ for (let i = 0; i < config.kvCount; i++) {
253
+ kvPairs.push({
254
+ x: kvStartX,
255
+ y: config.kvStartY + i * config.kvSpacingY,
256
+ id: i
257
+ });
258
+ }
259
+
260
+ // Initial Draw
261
+ drawQuery(query);
262
+ kvPairs.forEach((kv, i) => drawKVPair(kv, i));
263
+ currentStep = 1;
264
+ updateButtons();
265
+ setStatus('Initialized Query and KV Pairs.');
266
+ }
267
+
268
+ function computePartialStatesStep(p) {
269
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
270
+ drawQuery(query);
271
+ kvPairs.forEach((kv, i) => drawKVPair(kv, i));
272
+
273
+ // --- Partition 1 ---
274
+ const state1X = config.queryPos.x + 250;
275
+ const state1Y = config.kvStartY + (config.kvPartitionSize / 2 - 0.5) * config.kvSpacingY - config.stateBoxHeight / 2;
276
+ if (!states.partition1) {
277
+ // Simulate LSE and create a representative color for v
278
+ states.partition1 = { x: state1X, y: state1Y, s: Math.random() * 5 + 5, vColor: config.colors.partition1, label: `State 1..${config.kvPartitionSize}` };
279
+ }
280
+
281
+ // Draw arrows from Q to KV (Partition 1)
282
+ for (let i = 0; i < config.kvPartitionSize; i++) {
283
+ drawArrow(query.x + 15, query.y, kvPairs[i].x - 15, kvPairs[i].y, config.colors.partition1, p);
284
+ }
285
+ // Draw arrows from KV to State (Partition 1)
286
+ if (p >= 0.5) { // Start drawing state arrows halfway through
287
+ const stateProgress = (p - 0.5) * 2;
288
+ for (let i = 0; i < config.kvPartitionSize; i++) {
289
+ drawArrow(kvPairs[i].x + 15, kvPairs[i].y, states.partition1.x, states.partition1.y + config.stateBoxHeight / 2, config.colors.partition1, stateProgress);
290
+ }
291
+ // Draw state box with fade-in effect (using alpha)
292
+ ctx.globalAlpha = stateProgress;
293
+ drawAttentionState(states.partition1, config.colors.partition1);
294
+ ctx.globalAlpha = 1.0; // Reset alpha
295
+ }
296
+
297
+
298
+ // --- Partition 2 ---
299
+ const state2X = state1X; // Align horizontally
300
+ const state2Y = config.kvStartY + (config.kvPartitionSize + config.kvPartitionSize / 2 - 0.5) * config.kvSpacingY - config.stateBoxHeight / 2;
301
+ if (!states.partition2) {
302
+ states.partition2 = { x: state2X, y: state2Y, s: Math.random() * 5 + 5, vColor: config.colors.partition2, label: `State ${config.kvPartitionSize+1}..${config.kvCount}` };
303
+ }
304
+
305
+ // Draw arrows from Q to KV (Partition 2)
306
+ for (let i = config.kvPartitionSize; i < config.kvCount; i++) {
307
+ drawArrow(query.x + 15, query.y, kvPairs[i].x - 15, kvPairs[i].y, config.colors.partition2, p);
308
+ }
309
+ // Draw arrows from KV to State (Partition 2)
310
+ if (p >= 0.5) {
311
+ const stateProgress = (p - 0.5) * 2;
312
+ for (let i = config.kvPartitionSize; i < config.kvCount; i++) {
313
+ drawArrow(kvPairs[i].x + 15, kvPairs[i].y, states.partition2.x, states.partition2.y + config.stateBoxHeight / 2, config.colors.partition2, stateProgress);
314
+ }
315
+ // Draw state box with fade-in
316
+ ctx.globalAlpha = stateProgress;
317
+ drawAttentionState(states.partition2, config.colors.partition2);
318
+ ctx.globalAlpha = 1.0;
319
+ }
320
+ }
321
+
322
+ function mergeStatesStep(p) {
323
+ // Redraw previous step completely first
324
+ computePartialStatesStep(1);
325
+
326
+ // --- Merge Operation ---
327
+ const mergePointY = canvas.height / 2;
328
+ const finalStateY = mergePointY - config.stateBoxHeight / 2;
329
+
330
+ if (!states.final) {
331
+ // Simulate merged state calculation: s = log(e^s1 + e^s2), v is weighted average
332
+ const s_final = Math.log(Math.exp(states.partition1.s) + Math.exp(states.partition2.s));
333
+ // Simple color mixing for v visualization
334
+ const finalVColor = averageHexColors(states.partition1.vColor, states.partition2.vColor);
335
+ states.final = { x: config.finalStateX, y: finalStateY, s: s_final, vColor: finalVColor, label: `Final State (1..${config.kvCount})` };
336
+ }
337
+
338
+ // Draw arrows from partial states to merge point
339
+ const mergeArrowEndX = config.mergePointX - 10; // End slightly before the symbol
340
+ drawArrow(states.partition1.x + config.stateBoxWidth, states.partition1.y + config.stateBoxHeight / 2, mergeArrowEndX, mergePointY, config.colors.partition1, p);
341
+ drawArrow(states.partition2.x + config.stateBoxWidth, states.partition2.y + config.stateBoxHeight / 2, mergeArrowEndX, mergePointY, config.colors.partition2, p);
342
+
343
+ // Draw merge symbol (⊕) - appears halfway
344
+ if (p >= 0.5) {
345
+ const symbolProgress = (p - 0.5) * 2;
346
+ ctx.globalAlpha = symbolProgress;
347
+ ctx.font = 'bold 30px Inter';
348
+ ctx.fillStyle = config.colors.merged;
349
+ ctx.textAlign = 'center';
350
+ ctx.fillText('⊕', config.mergePointX, mergePointY + 10); // Adjust Y for vertical centering
351
+
352
+ // Draw arrow from merge symbol to final state
353
+ const finalArrowStartX = config.mergePointX + 15; // Start after the symbol
354
+ drawArrow(finalArrowStartX, mergePointY, states.final.x, states.final.y + config.stateBoxHeight / 2, config.colors.merged, symbolProgress);
355
+
356
+ // Draw final state box with fade-in
357
+ drawAttentionState(states.final, config.colors.merged);
358
+ ctx.globalAlpha = 1.0; // Reset alpha
359
+ }
360
+ }
361
+
362
+ // --- Helper Functions ---
363
+ function setStatus(text) {
364
+ statusText.textContent = text;
365
+ }
366
+
367
+ function updateButtons() {
368
+ resetBtn.disabled = false;
369
+ computeStatesBtn.disabled = currentStep < 1 || currentStep >= 2;
370
+ mergeStatesBtn.disabled = currentStep < 2 || currentStep >= 3;
371
+ }
372
+
373
+ // Simple hex color averaging for visualization
374
+ function averageHexColors(color1, color2) {
375
+ const c1 = parseInt(color1.substring(1), 16);
376
+ const c2 = parseInt(color2.substring(1), 16);
377
+
378
+ const r1 = (c1 >> 16) & 255;
379
+ const g1 = (c1 >> 8) & 255;
380
+ const b1 = c1 & 255;
381
+
382
+ const r2 = (c2 >> 16) & 255;
383
+ const g2 = (c2 >> 8) & 255;
384
+ const b2 = c2 & 255;
385
+
386
+ const rAvg = Math.round((r1 + r2) / 2);
387
+ const gAvg = Math.round((g1 + g2) / 2);
388
+ const bAvg = Math.round((b1 + b2) / 2);
389
+
390
+ return `#${(1 << 24 | rAvg << 16 | gAvg << 8 | bAvg).toString(16).slice(1).padStart(6, '0')}`;
391
+ }
392
+
393
+
394
+ // --- Event Listeners ---
395
+ resetBtn.addEventListener('click', () => {
396
+ initialize();
397
+ });
398
+
399
+ computeStatesBtn.addEventListener('click', () => {
400
+ if (currentStep === 1) {
401
+ setStatus('Computing partial attention states...');
402
+ animateStep(computePartialStatesStep, 2);
403
+ }
404
+ });
405
+
406
+ mergeStatesBtn.addEventListener('click', () => {
407
+ if (currentStep === 2) {
408
+ setStatus('Merging attention states...');
409
+ animateStep(mergeStatesStep, 3);
410
+ }
411
+ });
412
+
413
+ // --- Initial Setup ---
414
+ window.onload = () => {
415
+ // Adjust canvas size slightly for high DPI if needed, but keep logical size
416
+ const dpr = window.devicePixelRatio || 1;
417
+ const rect = canvas.getBoundingClientRect();
418
+ // canvas.width = rect.width * dpr; // Keep logical size for layout
419
+ // canvas.height = rect.height * dpr;
420
+ // ctx.scale(dpr, dpr); // Scale context instead
421
+
422
+ initialize(); // Draw initial state on load
423
+ };
424
+
425
+ // Optional: Redraw on resize
426
+ window.addEventListener('resize', () => {
427
+ // Basic redraw based on current step - could be more sophisticated
428
+ if (currentStep === 1) initialize();
429
+ else if (currentStep === 2) computePartialStatesStep(1); // Draw completed step
430
+ else if (currentStep === 3) mergeStatesStep(1); // Draw completed step
431
+ });
432
+
433
+ </script>
434
+
435
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=ucalyptus/flashinfer-attention-states-recursive-merge-operator-viz" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
436
  </html>
prompts.txt ADDED
File without changes