Javare commited on
Commit
8f5af2b
·
verified ·
1 Parent(s): 20396d0

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +284 -96
index.html CHANGED
@@ -3,15 +3,137 @@
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Ton application WebGPU</title>
7
 
8
  <script type="module" crossorigin src="/assets/index-Btti6dN2.js"></script>
9
  <link rel="stylesheet" crossorigin href="/assets/index-CjAcR-nm.css">
10
 
11
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  <script>
14
- // 1. On "intercepte" la création du Worker pour espionner ses messages
 
 
 
 
 
 
 
 
 
 
 
15
  const OriginalWorker = window.Worker;
16
  window.Worker = function(...args) {
17
  const worker = new OriginalWorker(...args);
@@ -19,120 +141,186 @@
19
  worker.addEventListener('message', function(e) {
20
  const data = e.data;
21
 
22
- // Quand la génération commence, on vide le graphique
23
- if (data && data.status === 'start' && window.tpsChart) {
24
- window.tpsChart.data.labels = [];
25
- window.tpsChart.data.datasets[0].data = [];
26
- window.tpsChart.update();
 
 
 
 
27
  }
28
 
29
- // Pendant la génération, on ajoute les points au graphique
30
- if (data && data.status === 'update' && data.tps !== undefined && window.tpsChart) {
31
- window.tpsChart.data.labels.push(data.numTokens);
32
- window.tpsChart.data.datasets[0].data.push(parseFloat(data.tps.toFixed(2)));
33
- window.tpsChart.update('none'); // 'none' pour de meilleures performances (pas d'animation)
 
 
 
 
 
 
 
 
 
 
 
34
  }
35
  });
36
 
37
  return worker;
38
  };
39
 
40
- // 2. On initialise le graphique une fois que la page est chargée
41
- document.addEventListener("DOMContentLoaded", () => {
42
- const ctx = document.getElementById('stressTestChart').getContext('2d');
43
- window.tpsChart = new Chart(ctx, {
44
- type: 'line',
45
- data: {
46
- labels: [],
47
- datasets: [{
48
- label: 'Vitesse (Tokens / Sec)',
49
- data: [],
50
- borderColor: '#4bc0c0',
51
- backgroundColor: 'rgba(75, 192, 192, 0.2)',
52
- borderWidth: 2,
53
- tension: 0.1, // Courbe légère
54
- pointRadius: 0 // Cache les ronds pour ne pas surcharger
55
- }]
56
- },
57
- options: {
58
- responsive: true,
59
- animation: false, // Désactive les animations pour le stress test
60
- scales: {
61
- x: { title: { display: true, text: 'Tokens générés' } },
62
- y: { title: { display: true, text: 'Tokens / Sec' }, beginAtZero: true }
63
- }
64
  }
65
- });
66
- });
67
- // Système de leaderboard global connecté à l'API globale
68
- async function loadLeaderboard() {
69
- try {
70
- const response = await fetch('/api/scores');
71
- const scores = await response.json();
 
 
 
 
 
 
 
 
 
 
 
72
 
73
- const tbody = document.getElementById('leaderboardBody');
74
- tbody.innerHTML = '';
 
 
75
 
76
- if(!scores || scores.length === 0) {
77
- tbody.innerHTML = '<tr><td colspan="5" style="text-align:center; color:#888;">Aucun score global pour le moment. Soyez le premier !</td></tr>';
78
- return;
79
- }
80
 
81
- scores.forEach((item, index) => {
82
- const row = document.createElement('tr');
83
- const isTop3 = index < 3 ? `<span class="badge-top">🥇 ${index + 1}</span>` : index + 1;
84
- row.innerHTML = `
85
- <td>${isTop3}</td>
86
- <td><strong>${item.config}</strong></td>
87
- <td>${item.browser}</td>
88
- <td>${item.power}</td>
89
- <td style="color: #2b6cb0; font-weight: bold;">${item.score.toFixed(2)} tps</td>
90
- `;
91
- tbody.appendChild(row);
92
- });
93
- } catch (error) {
94
- console.error("Erreur lors du chargement du classement global:", error);
95
- }
96
- }
97
 
98
- async function saveScore(finalScore) {
99
- if (finalScore <= 0) return;
100
-
101
- const type = document.getElementById('deviceType').value;
102
- const brand = document.getElementById('deviceBrand').value;
103
- const model = document.getElementById('deviceModel').value || "Modèle inconnu";
104
- const browser = document.getElementById('browserType').value;
105
-
106
- const configString = `${type} - ${brand} (${model})`;
107
-
108
- const payload = {
109
- config: configString,
110
- browser: browser,
111
- power: selectedPower,
112
- score: finalScore
113
- };
114
 
115
- try {
116
- // Envoi du score à notre API Python sécurisée
117
- await fetch('/api/score', {
118
- method: 'POST',
119
- headers: { 'Content-Type': 'application/json' },
120
- body: JSON.stringify(payload)
 
 
121
  });
122
- // Rechargement du classement mis à jour pour tout le monde
123
- loadLeaderboard();
124
- } catch (error) {
125
- console.error("Erreur lors de l'envoi du score au serveur:", error);
126
- }
127
- }
128
  </script>
129
  </head>
130
  <body>
 
131
  <div id="root"></div>
132
 
133
- <div style="width: 100%; max-width: 800px; margin: 40px auto; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
134
- <h3 style="text-align: center; font-family: sans-serif; color: #333;">Stress Test WebGPU : Vitesse en temps réel</h3>
135
- <canvas id="stressTestChart"></canvas>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  </div>
 
137
  </body>
138
  </html>
 
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Local AI WebGPU - Benchmark</title>
7
 
8
  <script type="module" crossorigin src="/assets/index-Btti6dN2.js"></script>
9
  <link rel="stylesheet" crossorigin href="/assets/index-CjAcR-nm.css">
10
 
11
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
12
 
13
+ <style>
14
+ /* Style du panneau flottant à droite */
15
+ .benchmark-sidebar {
16
+ position: fixed;
17
+ top: 0;
18
+ right: 0;
19
+ width: 380px;
20
+ height: 100vh;
21
+ background: #ffffff;
22
+ box-shadow: -4px 0 25px rgba(0,0,0,0.15);
23
+ z-index: 999999; /* S'assure de passer au-dessus de l'IA */
24
+ font-family: system-ui, -apple-system, sans-serif;
25
+ display: flex;
26
+ flex-direction: column;
27
+ color: #2d3748;
28
+ border-left: 1px solid #e2e8f0;
29
+ }
30
+
31
+ .benchmark-header {
32
+ background: #1a202c;
33
+ color: white;
34
+ padding: 15px;
35
+ text-align: center;
36
+ margin: 0;
37
+ font-size: 1.2rem;
38
+ }
39
+
40
+ .benchmark-content {
41
+ padding: 20px;
42
+ overflow-y: auto;
43
+ flex: 1;
44
+ }
45
+
46
+ .form-group {
47
+ margin-bottom: 12px;
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: 4px;
51
+ }
52
+
53
+ .form-group label {
54
+ font-size: 0.85rem;
55
+ font-weight: 600;
56
+ color: #4a5568;
57
+ }
58
+
59
+ .form-group input, .form-group select {
60
+ padding: 8px 12px;
61
+ border: 1px solid #cbd5e0;
62
+ border-radius: 6px;
63
+ font-size: 0.9rem;
64
+ background: #f7fafc;
65
+ }
66
+
67
+ .power-selector {
68
+ display: flex;
69
+ gap: 10px;
70
+ margin-top: 4px;
71
+ }
72
+
73
+ .power-btn {
74
+ flex: 1;
75
+ padding: 10px;
76
+ border: 2px solid #e2e8f0;
77
+ border-radius: 6px;
78
+ background: #fff;
79
+ cursor: pointer;
80
+ font-weight: bold;
81
+ transition: all 0.2s;
82
+ font-size: 0.85rem;
83
+ }
84
+
85
+ .power-btn.active {
86
+ border-color: #3182ce;
87
+ background: #ebf8ff;
88
+ color: #2b6cb0;
89
+ }
90
+
91
+ /* Style du tableau des scores */
92
+ .leaderboard-table {
93
+ width: 100%;
94
+ border-collapse: collapse;
95
+ margin-top: 15px;
96
+ font-size: 0.85rem;
97
+ }
98
+
99
+ .leaderboard-table th, .leaderboard-table td {
100
+ padding: 8px;
101
+ text-align: left;
102
+ border-bottom: 1px solid #e2e8f0;
103
+ }
104
+
105
+ .leaderboard-table th {
106
+ background-color: #edf2f7;
107
+ font-weight: 700;
108
+ }
109
+
110
+ .badge-top {
111
+ background: #ecc94b;
112
+ color: #744210;
113
+ padding: 1px 4px;
114
+ border-radius: 4px;
115
+ font-weight: bold;
116
+ }
117
+
118
+ /* Ajustement pour que l'application principale ne soit pas cachée derrière le panneau */
119
+ body {
120
+ margin-right: 380px !important;
121
+ }
122
+ </style>
123
+
124
  <script>
125
+ let selectedPower = '🔌 Secteur';
126
+ let maxTpsRecorded = 0;
127
+ let tokenLabels = [];
128
+ let tpsData = [];
129
+
130
+ function setPower(type) {
131
+ selectedPower = type;
132
+ document.getElementById('btnSecteur').classList.toggle('active', type === '🔌 Secteur');
133
+ document.getElementById('btnBatterie').classList.toggle('active', type === '🔋 Batterie');
134
+ }
135
+
136
+ // Interception magique du Web Worker pour le graphique et les performances
137
  const OriginalWorker = window.Worker;
138
  window.Worker = function(...args) {
139
  const worker = new OriginalWorker(...args);
 
141
  worker.addEventListener('message', function(e) {
142
  const data = e.data;
143
 
144
+ if (data && data.status === 'start') {
145
+ maxTpsRecorded = 0;
146
+ tokenLabels = [];
147
+ tpsData = [];
148
+ if (window.tpsChart) {
149
+ window.tpsChart.data.labels = [];
150
+ window.tpsChart.data.datasets[0].data = [];
151
+ window.tpsChart.update();
152
+ }
153
  }
154
 
155
+ if (data && data.status === 'update' && data.tps !== undefined) {
156
+ if (data.tps > maxTpsRecorded) {
157
+ maxTpsRecorded = data.tps;
158
+ }
159
+ tokenLabels.push(data.numTokens);
160
+ tpsData.push(parseFloat(data.tps.toFixed(2)));
161
+ if (window.tpsChart) {
162
+ window.tpsChart.data.labels = tokenLabels;
163
+ window.tpsChart.data.datasets[0].data = tpsData;
164
+ window.tpsChart.update('none');
165
+ }
166
+ }
167
+
168
+ // Envoi automatique du score à l'API globale en fin de génération
169
+ if (data && data.status === 'complete') {
170
+ saveScore(maxTpsRecorded);
171
  }
172
  });
173
 
174
  return worker;
175
  };
176
 
177
+ // Appels API vers le serveur Python (FastAPI)
178
+ async function loadLeaderboard() {
179
+ try {
180
+ const response = await fetch('/api/scores');
181
+ const scores = await response.json();
182
+
183
+ const tbody = document.getElementById('leaderboardBody');
184
+ tbody.innerHTML = '';
185
+
186
+ if(!scores || scores.length === 0) {
187
+ tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; color:#888;">Aucun score global.</td></tr>';
188
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
189
  }
190
+
191
+ scores.forEach((item, index) => {
192
+ const row = document.createElement('tr');
193
+ const isTop3 = index < 3 ? `<span class="badge-top">🥇 ${index + 1}</span>` : index + 1;
194
+ row.innerHTML = `
195
+ <td>${isTop3}</td>
196
+ <td><strong>${item.config}</strong><br><small style="color:#718096">${item.browser} ${item.power}</small></td>
197
+ <td style="color: #2b6cb0; font-weight: bold; text-align: right;">${item.score.toFixed(1)} tps</td>
198
+ `;
199
+ tbody.appendChild(row);
200
+ });
201
+ } catch (error) {
202
+ console.error("Erreur classement global:", error);
203
+ }
204
+ }
205
+
206
+ async function saveScore(finalScore) {
207
+ if (finalScore <= 0) return;
208
 
209
+ const type = document.getElementById('deviceType').value;
210
+ const model = document.getElementById('deviceModel').value || "Inconnu";
211
+ const os = document.getElementById('osType').value;
212
+ const browser = document.getElementById('browserType').value;
213
 
214
+ // Structure propre de la configuration de l'appareil
215
+ const configString = `${type} [${os}] (${model})`;
 
 
216
 
217
+ const payload = {
218
+ config: configString,
219
+ browser: browser,
220
+ power: selectedPower,
221
+ score: finalScore
222
+ };
 
 
 
 
 
 
 
 
 
 
223
 
224
+ try {
225
+ await fetch('/api/score', {
226
+ method: 'POST',
227
+ headers: { 'Content-Type': 'application/json' },
228
+ body: JSON.stringify(payload)
229
+ });
230
+ loadLeaderboard();
231
+ } catch (error) {
232
+ console.error("Erreur envoi score:", error);
233
+ }
234
+ }
 
 
 
 
 
235
 
236
+ document.addEventListener("DOMContentLoaded", () => {
237
+ loadLeaderboard();
238
+
239
+ const ctx = document.getElementById('stressTestChart').getContext('2d');
240
+ window.tpsChart = new Chart(ctx, {
241
+ type: 'line',
242
+ data: { labels: [], datasets: [{ label: 'Tokens/Sec', data: [], borderColor: '#3182ce', tension: 0.1, pointRadius: 0, borderWidth: 2 }] },
243
+ options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true } } }
244
  });
245
+ });
 
 
 
 
 
246
  </script>
247
  </head>
248
  <body>
249
+
250
  <div id="root"></div>
251
 
252
+ <div class="benchmark-sidebar">
253
+ <h3 class="benchmark-header"> WebGPU Stress Test</h3>
254
+
255
+ <div class="benchmark-content">
256
+ <div class="form-group">
257
+ <label>Type d'appareil</label>
258
+ <select id="deviceType">
259
+ <option value="💻 UC (Bureau)">💻 UC (Bureau)</option>
260
+ <option value="💻 Laptop">💻 Laptop</option>
261
+ <option value="🖥️ All in One">🖥️ All in One</option>
262
+ <option value="📱 Tablette">📱 Tablette</option>
263
+ <option value="📱 SmartPhone">📱 SmartPhone</option>
264
+ </select>
265
+ </div>
266
+
267
+ <div class="form-group">
268
+ <label>Modèle (CPU / GPU / Modèle exact)</label>
269
+ <input type="text" id="deviceModel" placeholder="Ex: RTX 4070 / Apple M3 / Galaxy S24" required>
270
+ </div>
271
+
272
+ <div class="form-group">
273
+ <label>Système d'exploitation (OS)</label>
274
+ <select id="osType">
275
+ <option value="Windows">Windows</option>
276
+ <option value="macOS">macOS</option>
277
+ <option value="Linux">Linux</option>
278
+ <option value="Android">Android</option>
279
+ <option value="iOS">iOS</option>
280
+ </select>
281
+ </div>
282
+
283
+ <div class="form-group">
284
+ <label>Navigateur Internet</label>
285
+ <select id="browserType">
286
+ <option value="Chrome">Chrome</option>
287
+ <option value="Firefox">Firefox</option>
288
+ <option value="Edge">Edge</option>
289
+ <option value="Brave">Brave</option>
290
+ <option value="Safari">Safari</option>
291
+ <option value="Opera">Opera</option>
292
+ </select>
293
+ </div>
294
+
295
+ <div class="form-group">
296
+ <label>Source d'alimentation</label>
297
+ <div class="power-selector">
298
+ <button type="button" class="power-btn active" id="btnSecteur" onclick="setPower('🔌 Secteur')">🔌 Secteur</button>
299
+ <button type="button" class="power-btn" id="btnBatterie" onclick="setPower('🔋 Batterie')">🔋 Batterie</button>
300
+ </div>
301
+ </div>
302
+
303
+ <hr style="border: 0; border-top: 1px solid #e2e8f0; margin: 15px 0;">
304
+
305
+ <label style="font-size: 0.85rem; font-weight: 600; color: #4a5568; display:block; margin-bottom:5px;">Performances graphiques (tps)</label>
306
+ <div style="height: 140px; position: relative; margin-bottom: 20px;">
307
+ <canvas id="stressTestChart"></canvas>
308
+ </div>
309
+
310
+ <h4 style="margin: 15px 0 5px 0; color: #1a202c;">🏆 TOP 10 Global</h4>
311
+ <table class="leaderboard-table">
312
+ <thead>
313
+ <tr>
314
+ <th>Rang</th>
315
+ <th>Configuration</th>
316
+ <th style="text-align: right;">Vitesse</th>
317
+ </tr>
318
+ </thead>
319
+ <tbody id="leaderboardBody">
320
+ </tbody>
321
+ </table>
322
+ </div>
323
  </div>
324
+
325
  </body>
326
  </html>