danielrosehill commited on
Commit
261f212
·
1 Parent(s): fca700e
Files changed (3) hide show
  1. app.js +200 -0
  2. index.html +57 -0
  3. style.css +42 -1
app.js CHANGED
@@ -76,6 +76,8 @@ function initializeApp() {
76
  setupSorting();
77
  setupTabs();
78
  createQuadrantChart();
 
 
79
  }
80
 
81
  // Setup tab navigation
@@ -395,5 +397,203 @@ function setupTableControls() {
395
  quadrantFilter.addEventListener('change', applyFilters);
396
  }
397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  // Initialize on page load
399
  document.addEventListener('DOMContentLoaded', loadData);
 
76
  setupSorting();
77
  setupTabs();
78
  createQuadrantChart();
79
+ createAllQuadrantsChart();
80
+ createIndividualQuadrantCharts();
81
  }
82
 
83
  // Setup tab navigation
 
397
  quadrantFilter.addEventListener('change', applyFilters);
398
  }
399
 
400
+ // Create all quadrants overview chart (same as main quadrant chart)
401
+ function createAllQuadrantsChart() {
402
+ const ctx = document.getElementById('allQuadrantsChart').getContext('2d');
403
+
404
+ const quadrantColors = {
405
+ 'Low Cost / High Context': '#10b981',
406
+ 'High Cost / High Context': '#2563eb',
407
+ 'Low Cost / Low Context': '#f59e0b',
408
+ 'High Cost / Low Context': '#ef4444'
409
+ };
410
+
411
+ const datasets = Object.keys(quadrantColors).map(quadrant => {
412
+ const models = allModels.filter(m => m.quadrant === quadrant);
413
+ return {
414
+ label: quadrant,
415
+ data: models.map(m => ({
416
+ x: m.avg_cost,
417
+ y: m.context_length / 1000,
418
+ model: m
419
+ })),
420
+ backgroundColor: quadrantColors[quadrant] + '80',
421
+ borderColor: quadrantColors[quadrant],
422
+ borderWidth: 2,
423
+ pointRadius: 5,
424
+ pointHoverRadius: 8
425
+ };
426
+ });
427
+
428
+ const medianCost = median(allModels.map(m => m.avg_cost));
429
+ const medianContext = median(allModels.map(m => m.context_length / 1000));
430
+
431
+ new Chart(ctx, {
432
+ type: 'scatter',
433
+ data: { datasets },
434
+ options: {
435
+ responsive: true,
436
+ maintainAspectRatio: false,
437
+ plugins: {
438
+ title: {
439
+ display: false
440
+ },
441
+ legend: {
442
+ display: true,
443
+ position: 'top'
444
+ },
445
+ tooltip: {
446
+ callbacks: {
447
+ label: function(context) {
448
+ const model = context.raw.model;
449
+ return [
450
+ model.model_name,
451
+ `Vendor: ${model.displayVendor}`,
452
+ `Context: ${(model.context_length / 1000).toFixed(0)}K tokens`,
453
+ `Input: $${model.input_price_usd_per_m.toFixed(2)}/M`,
454
+ `Output: $${model.output_price_usd_per_m.toFixed(2)}/M`,
455
+ `Avg: $${model.avg_cost.toFixed(2)}/M`
456
+ ];
457
+ }
458
+ }
459
+ },
460
+ annotation: {
461
+ annotations: {
462
+ verticalLine: {
463
+ type: 'line',
464
+ xMin: medianCost,
465
+ xMax: medianCost,
466
+ borderColor: '#64748b',
467
+ borderWidth: 2,
468
+ borderDash: [5, 5],
469
+ label: {
470
+ display: true,
471
+ content: `Median: $${medianCost.toFixed(2)}`,
472
+ position: 'start'
473
+ }
474
+ },
475
+ horizontalLine: {
476
+ type: 'line',
477
+ yMin: medianContext,
478
+ yMax: medianContext,
479
+ borderColor: '#64748b',
480
+ borderWidth: 2,
481
+ borderDash: [5, 5],
482
+ label: {
483
+ display: true,
484
+ content: `Median: ${medianContext.toFixed(0)}K`,
485
+ position: 'end'
486
+ }
487
+ }
488
+ }
489
+ }
490
+ },
491
+ scales: {
492
+ x: {
493
+ type: 'logarithmic',
494
+ title: {
495
+ display: true,
496
+ text: 'Average Cost ($/M tokens, log scale)'
497
+ },
498
+ min: 0.01
499
+ },
500
+ y: {
501
+ title: {
502
+ display: true,
503
+ text: 'Context Window (K tokens)'
504
+ }
505
+ }
506
+ }
507
+ },
508
+ plugins: [window['chartjs-plugin-annotation']]
509
+ });
510
+ }
511
+
512
+ // Create individual quadrant zoom charts
513
+ function createIndividualQuadrantCharts() {
514
+ const quadrants = [
515
+ { name: 'Low Cost / High Context', id: 'lchc', color: '#10b981' },
516
+ { name: 'High Cost / High Context', id: 'hchc', color: '#2563eb' },
517
+ { name: 'Low Cost / Low Context', id: 'lclc', color: '#f59e0b' },
518
+ { name: 'High Cost / Low Context', id: 'hclc', color: '#ef4444' }
519
+ ];
520
+
521
+ quadrants.forEach(quadrant => {
522
+ const ctx = document.getElementById(`quadrant-${quadrant.id}`).getContext('2d');
523
+ const models = allModels.filter(m => m.quadrant === quadrant.name);
524
+
525
+ if (models.length === 0) return;
526
+
527
+ const data = models.map(m => ({
528
+ x: m.avg_cost,
529
+ y: m.context_length / 1000,
530
+ model: m
531
+ }));
532
+
533
+ new Chart(ctx, {
534
+ type: 'scatter',
535
+ data: {
536
+ datasets: [{
537
+ label: quadrant.name,
538
+ data: data,
539
+ backgroundColor: quadrant.color + '80',
540
+ borderColor: quadrant.color,
541
+ borderWidth: 2,
542
+ pointRadius: 6,
543
+ pointHoverRadius: 9
544
+ }]
545
+ },
546
+ options: {
547
+ responsive: true,
548
+ maintainAspectRatio: false,
549
+ plugins: {
550
+ title: {
551
+ display: false
552
+ },
553
+ legend: {
554
+ display: false
555
+ },
556
+ tooltip: {
557
+ callbacks: {
558
+ label: function(context) {
559
+ const model = context.raw.model;
560
+ return [
561
+ model.model_name,
562
+ `Vendor: ${model.displayVendor}`,
563
+ `Context: ${(model.context_length / 1000).toFixed(0)}K tokens`,
564
+ `Input: $${model.input_price_usd_per_m.toFixed(2)}/M`,
565
+ `Output: $${model.output_price_usd_per_m.toFixed(2)}/M`,
566
+ `Avg: $${model.avg_cost.toFixed(2)}/M`
567
+ ];
568
+ }
569
+ }
570
+ }
571
+ },
572
+ scales: {
573
+ x: {
574
+ type: 'logarithmic',
575
+ title: {
576
+ display: true,
577
+ text: 'Average Cost ($/M tokens, log scale)',
578
+ font: {
579
+ size: 11
580
+ }
581
+ }
582
+ },
583
+ y: {
584
+ title: {
585
+ display: true,
586
+ text: 'Context (K tokens)',
587
+ font: {
588
+ size: 11
589
+ }
590
+ }
591
+ }
592
+ }
593
+ }
594
+ });
595
+ });
596
+ }
597
+
598
  // Initialize on page load
599
  document.addEventListener('DOMContentLoaded', loadData);
index.html CHANGED
@@ -66,6 +66,7 @@
66
 
67
  <div class="tabs">
68
  <button class="tab-button active" data-tab="models">All Models</button>
 
69
  <button class="tab-button" data-tab="analysis">Visual Analysis</button>
70
  </div>
71
 
@@ -135,6 +136,62 @@
135
  </section>
136
  </div>
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  <div class="tab-content" id="tab-analysis">
139
  <section class="data-notes">
140
  <h3>Analysis Notes</h3>
 
66
 
67
  <div class="tabs">
68
  <button class="tab-button active" data-tab="models">All Models</button>
69
+ <button class="tab-button" data-tab="quadrants">Quadrant Analysis</button>
70
  <button class="tab-button" data-tab="analysis">Visual Analysis</button>
71
  </div>
72
 
 
136
  </section>
137
  </div>
138
 
139
+ <div class="tab-content" id="tab-quadrants">
140
+ <section class="data-notes">
141
+ <h3>Quadrant Analysis</h3>
142
+ <ul>
143
+ <li><strong>Overview:</strong> Models divided into four quadrants based on median cost and context values</li>
144
+ <li><strong>Green (Low Cost / High Context):</strong> Best value - low prices with large context windows</li>
145
+ <li><strong>Blue (High Cost / High Context):</strong> Premium models with extensive context capabilities</li>
146
+ <li><strong>Orange (Low Cost / Low Context):</strong> Budget-friendly for simple tasks</li>
147
+ <li><strong>Red (High Cost / Low Context):</strong> Specialized models with limited context</li>
148
+ </ul>
149
+ </section>
150
+
151
+ <section class="chart-section">
152
+ <h2>All Quadrants Overview</h2>
153
+ <p>Complete view showing all models across quadrants with median division lines.</p>
154
+ <div class="chart-container">
155
+ <canvas id="allQuadrantsChart"></canvas>
156
+ </div>
157
+ </section>
158
+
159
+ <section class="quadrant-details">
160
+ <h2>Individual Quadrant Deep Dive</h2>
161
+ <p>Detailed view of each quadrant's models for easier comparison within each category.</p>
162
+
163
+ <div class="quadrant-grid">
164
+ <div class="quadrant-detail">
165
+ <h3 style="color: #10b981;">Low Cost / High Context</h3>
166
+ <div class="chart-container-small">
167
+ <canvas id="quadrant-lchc"></canvas>
168
+ </div>
169
+ </div>
170
+
171
+ <div class="quadrant-detail">
172
+ <h3 style="color: #2563eb;">High Cost / High Context</h3>
173
+ <div class="chart-container-small">
174
+ <canvas id="quadrant-hchc"></canvas>
175
+ </div>
176
+ </div>
177
+
178
+ <div class="quadrant-detail">
179
+ <h3 style="color: #f59e0b;">Low Cost / Low Context</h3>
180
+ <div class="chart-container-small">
181
+ <canvas id="quadrant-lclc"></canvas>
182
+ </div>
183
+ </div>
184
+
185
+ <div class="quadrant-detail">
186
+ <h3 style="color: #ef4444;">High Cost / Low Context</h3>
187
+ <div class="chart-container-small">
188
+ <canvas id="quadrant-hclc"></canvas>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ </section>
193
+ </div>
194
+
195
  <div class="tab-content" id="tab-analysis">
196
  <section class="data-notes">
197
  <h3>Analysis Notes</h3>
style.css CHANGED
@@ -212,6 +212,12 @@ h3 {
212
  margin-top: 1.5rem;
213
  }
214
 
 
 
 
 
 
 
215
  .dual-chart {
216
  display: grid;
217
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
@@ -230,11 +236,29 @@ h3 {
230
  /* Quadrant Grid */
231
  .quadrant-grid {
232
  display: grid;
233
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
234
  gap: 1.5rem;
235
  margin-top: 1.5rem;
236
  }
237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  .quadrant-card {
239
  background: var(--bg);
240
  border: 2px solid var(--border);
@@ -567,7 +591,24 @@ footer a:hover {
567
  height: 350px;
568
  }
569
 
 
 
 
 
570
  .dual-chart {
571
  grid-template-columns: 1fr;
572
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  }
 
212
  margin-top: 1.5rem;
213
  }
214
 
215
+ .chart-container-small {
216
+ position: relative;
217
+ height: 300px;
218
+ margin-top: 1rem;
219
+ }
220
+
221
  .dual-chart {
222
  display: grid;
223
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
 
236
  /* Quadrant Grid */
237
  .quadrant-grid {
238
  display: grid;
239
+ grid-template-columns: repeat(2, 1fr);
240
  gap: 1.5rem;
241
  margin-top: 1.5rem;
242
  }
243
 
244
+ .quadrant-detail {
245
+ background: var(--bg);
246
+ border: 2px solid var(--border);
247
+ border-radius: 8px;
248
+ padding: 1.5rem;
249
+ }
250
+
251
+ .quadrant-detail h3 {
252
+ font-size: 1.1rem;
253
+ margin-bottom: 0.5rem;
254
+ padding-bottom: 0.75rem;
255
+ border-bottom: 2px solid var(--border);
256
+ }
257
+
258
+ .quadrant-details {
259
+ margin-top: 2rem;
260
+ }
261
+
262
  .quadrant-card {
263
  background: var(--bg);
264
  border: 2px solid var(--border);
 
591
  height: 350px;
592
  }
593
 
594
+ .chart-container-small {
595
+ height: 250px;
596
+ }
597
+
598
  .dual-chart {
599
  grid-template-columns: 1fr;
600
  }
601
+
602
+ .quadrant-grid {
603
+ grid-template-columns: 1fr;
604
+ }
605
+
606
+ .tabs {
607
+ flex-wrap: wrap;
608
+ }
609
+
610
+ .tab-button {
611
+ padding: 0.75rem 1rem;
612
+ font-size: 0.9rem;
613
+ }
614
  }