victor HF Staff commited on
Commit
55033ac
·
verified ·
1 Parent(s): b9f7f6c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +317 -93
index.html CHANGED
@@ -4,109 +4,309 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Provider Inference Metrics Dashboard</title>
7
- <!-- Include Plotly.js via CDN -->
8
  <script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
9
  <style>
 
 
 
 
 
 
 
 
 
 
10
  body {
11
- font-family: sans-serif;
12
- margin: 20px;
13
- background-color: #f4f4f4;
 
 
 
 
 
 
 
14
  }
15
  h1 {
16
  text-align: center;
17
- color: #333;
 
 
18
  }
19
- .dashboard-container {
20
  display: grid;
21
- grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); /* Responsive grid */
22
- gap: 20px; /* Space between plots */
 
 
 
 
23
  padding: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
  .plot-container {
26
- background-color: #fff;
27
- padding: 15px;
28
  border-radius: 8px;
29
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
30
- min-height: 400px; /* Ensure plots have some height */
 
 
 
 
 
 
 
 
 
31
  }
 
32
  #loading, #error {
 
33
  text-align: center;
34
  font-size: 1.2em;
35
- padding: 30px;
36
- color: #555;
37
  }
38
  #error {
39
- color: red;
40
- font-weight: bold;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
  </style>
43
  </head>
44
  <body>
45
 
46
- <h1>Provider Inference Metrics Dashboard</h1>
47
-
48
- <div id="loading">Loading data... Please wait.</div>
49
- <div id="error" style="display: none;"></div>
50
-
51
- <div class="dashboard-container">
52
- <div id="plotLatencyProvider" class="plot-container"></div>
53
- <div id="plotReliabilityProvider" class="plot-container"></div>
54
- <div id="plotLatencyModel" class="plot-container"></div>
55
- <div id="plotErrorTypesProvider" class="plot-container"></div>
56
- <div id="plotLatencyHeatmap" class="plot-container"></div>
57
- <!-- Add more divs here for additional plots -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  </div>
59
 
60
  <script>
61
  document.addEventListener('DOMContentLoaded', function() {
62
- const apiUrl = "https://datasets-server.huggingface.co/rows?dataset=victor%2Fproviders-metrics&config=default&split=train&offset=0&length=100"; // Fetch 1000 rows
63
  const loadingDiv = document.getElementById('loading');
64
  const errorDiv = document.getElementById('error');
 
65
  const dashboardContainer = document.querySelector('.dashboard-container');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- // Initially hide the dashboard container
68
- dashboardContainer.style.display = 'none';
69
 
70
  fetch(apiUrl)
71
  .then(response => {
72
  if (!response.ok) {
73
- throw new Error(`HTTP error! status: ${response.status}`);
74
  }
75
  return response.json();
76
  })
77
  .then(data => {
78
  loadingDiv.style.display = 'none'; // Hide loading message
79
- dashboardContainer.style.display = 'grid'; // Show dashboard
 
 
80
 
81
  // Extract the actual row data
82
  const rows = data.rows.map(item => item.row);
83
  console.log(`Fetched ${rows.length} rows.`);
 
84
 
85
- // --- Data Processing and Plotting ---
 
86
 
87
- // 1. Latency by Provider (Box Plot)
88
  createLatencyByProviderPlot(rows);
89
-
90
- // 2. Reliability by Provider (Stacked Bar)
91
  createReliabilityByProviderPlot(rows);
92
-
93
- // 3. Latency by Model (Box Plot)
94
  createLatencyByModelPlot(rows);
95
-
96
- // 4. Error Types by Provider (Grouped Bar)
97
  createErrorTypesByProviderPlot(rows);
98
-
99
- // 5. Latency Heatmap (Model vs Provider)
100
  createLatencyHeatmap(rows);
101
 
102
  })
103
  .catch(error => {
104
  console.error('Error fetching or processing data:', error);
105
  loadingDiv.style.display = 'none';
106
- errorDiv.textContent = `Error loading data: ${error.message}. Please check the console for details.`;
107
  errorDiv.style.display = 'block';
108
  });
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  // --- Plotting Functions ---
111
 
112
  function createLatencyByProviderPlot(rows) {
@@ -115,27 +315,27 @@
115
  if (!dataByProvider[row.provider_name]) {
116
  dataByProvider[row.provider_name] = [];
117
  }
118
- // Only include successful requests or requests with valid duration
119
  if (row.duration_ms !== null && row.duration_ms >= 0) {
120
  dataByProvider[row.provider_name].push(row.duration_ms);
121
  }
122
  });
123
 
124
- const plotData = Object.keys(dataByProvider).map(provider => ({
125
  y: dataByProvider[provider],
126
  type: 'box',
127
  name: provider,
128
- boxpoints: 'Outliers' // Show outliers
 
129
  }));
130
 
131
- const layout = {
132
- title: 'Latency Distribution by Provider (ms)',
133
- yaxis: { title: 'Duration (ms)', type: 'log' }, // Log scale often helps with latency
134
- xaxis: { title: 'Provider' },
135
- margin: { l: 50, r: 20, b: 100, t: 50 } // Adjust margins
136
- };
137
 
138
- Plotly.newPlot('plotLatencyProvider', plotData, layout);
139
  }
140
 
141
  function createReliabilityByProviderPlot(rows) {
@@ -145,7 +345,7 @@
145
 
146
  rows.forEach(row => {
147
  const provider = row.provider_name;
148
- const status = row.response_status_code;
149
  allProviders.add(provider);
150
  allStatusCodes.add(status);
151
 
@@ -159,26 +359,34 @@
159
  });
160
 
161
  const sortedProviders = Array.from(allProviders).sort();
162
- const sortedStatusCodes = Array.from(allStatusCodes).sort((a, b) => a - b); // Sort numerically
 
 
 
 
 
 
 
163
 
164
  const plotData = sortedStatusCodes.map(status => {
165
  return {
166
  x: sortedProviders,
167
  y: sortedProviders.map(provider => statusCountsByProvider[provider]?.[status] || 0),
168
  name: `Status ${status}`,
169
- type: 'bar'
 
170
  };
171
  });
172
 
173
- const layout = {
174
- title: 'Request Status Codes by Provider',
175
  barmode: 'stack',
176
- xaxis: { title: 'Provider' },
177
- yaxis: { title: 'Number of Requests' },
178
- margin: { l: 50, r: 20, b: 100, t: 50 }
179
- };
180
 
181
- Plotly.newPlot('plotReliabilityProvider', plotData, layout);
182
  }
183
 
184
  function createLatencyByModelPlot(rows) {
@@ -188,30 +396,30 @@
188
  if (!dataByModel[model]) {
189
  dataByModel[model] = [];
190
  }
191
- // Only include successful requests or requests with valid duration
192
  if (row.duration_ms !== null && row.duration_ms >= 0) {
193
  dataByModel[model].push(row.duration_ms);
194
  }
195
  });
196
 
197
- const plotData = Object.keys(dataByModel).map(model => ({
198
  y: dataByModel[model],
199
  type: 'box',
200
  name: model,
201
- boxpoints: 'Outliers'
 
202
  }));
203
 
204
- const layout = {
205
- title: 'Latency Distribution by Model (ms)',
206
- yaxis: { title: 'Duration (ms)', type: 'log' },
207
  xaxis: {
208
  title: 'Model ID',
209
- tickangle: -45 // Angle labels if they overlap
210
  },
211
- margin: { l: 50, r: 20, b: 150, t: 50 } // More bottom margin for angled labels
212
- };
213
 
214
- Plotly.newPlot('plotLatencyModel', plotData, layout);
215
  }
216
 
217
  function createErrorTypesByProviderPlot(rows) {
@@ -244,19 +452,20 @@
244
  x: sortedProviders,
245
  y: sortedProviders.map(provider => errorCountsByProvider[provider]?.[status] || 0),
246
  name: `Error ${status}`,
247
- type: 'bar'
 
248
  };
249
  });
250
 
251
- const layout = {
252
- title: 'Error Types by Provider (Non-200 Status)',
253
  barmode: 'group', // Group bars side-by-side for comparison
254
- xaxis: { title: 'Provider' },
255
- yaxis: { title: 'Number of Errors' },
256
- margin: { l: 50, r: 20, b: 100, t: 50 }
257
- };
258
 
259
- Plotly.newPlot('plotErrorTypesProvider', plotData, layout);
260
  }
261
 
262
  function createLatencyHeatmap(rows) {
@@ -285,28 +494,43 @@
285
  const zValues = sortedModels.map(model => {
286
  return sortedProviders.map(provider => {
287
  const data = latencyData[provider]?.[model];
288
- return data && data.count > 0 ? data.sum / data.count : null; // Calculate average, handle missing data
 
289
  });
290
  });
291
 
 
 
 
 
 
 
 
 
 
 
292
  const plotData = [{
293
  z: zValues,
294
  x: sortedProviders,
295
  y: sortedModels,
296
  type: 'heatmap',
297
- hoverongaps: false, // Don't show tooltips for empty cells
298
- colorscale: 'Viridis', // Choose a colorscale
299
- colorbar: { title: 'Avg Latency (ms)'}
 
 
 
 
300
  }];
301
 
302
- const layout = {
303
- title: 'Average Latency (ms) - Model vs. Provider',
304
- xaxis: { title: 'Provider', side: 'top' }, // Move x-axis labels to top
305
- yaxis: { title: 'Model ID' },
306
- margin: { l: 250, r: 50, b: 50, t: 100 } // Adjust margins for labels
307
- };
308
 
309
- Plotly.newPlot('plotLatencyHeatmap', plotData, layout);
310
  }
311
 
312
  });
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Provider Inference Metrics Dashboard</title>
 
7
  <script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
8
  <style>
9
+ :root {
10
+ --bg-color: #f8f9fa;
11
+ --card-bg-color: #ffffff;
12
+ --text-color: #212529;
13
+ --muted-text-color: #6c757d;
14
+ --border-color: #dee2e6;
15
+ --shadow-color: rgba(0, 0, 0, 0.05);
16
+ --primary-color: #0d6efd;
17
+ --plot-colorway: ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']; /* Plotly default */
18
+ }
19
  body {
20
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
21
+ margin: 0;
22
+ background-color: var(--bg-color);
23
+ color: var(--text-color);
24
+ line-height: 1.5;
25
+ }
26
+ .container {
27
+ max-width: 1600px;
28
+ margin: 20px auto;
29
+ padding: 0 20px;
30
  }
31
  h1 {
32
  text-align: center;
33
+ color: var(--text-color);
34
+ margin-bottom: 30px;
35
+ font-weight: 500;
36
  }
37
+ .kpi-container {
38
  display: grid;
39
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
40
+ gap: 20px;
41
+ margin-bottom: 30px;
42
+ }
43
+ .kpi-card {
44
+ background-color: var(--card-bg-color);
45
  padding: 20px;
46
+ border-radius: 8px;
47
+ box-shadow: 0 4px 8px var(--shadow-color);
48
+ text-align: center;
49
+ border: 1px solid var(--border-color);
50
+ }
51
+ .kpi-card h3 {
52
+ margin-top: 0;
53
+ margin-bottom: 10px;
54
+ font-size: 1rem;
55
+ color: var(--muted-text-color);
56
+ font-weight: 400;
57
+ }
58
+ .kpi-card .value {
59
+ font-size: 1.8rem;
60
+ font-weight: 600;
61
+ color: var(--text-color);
62
+ }
63
+ .kpi-card .unit {
64
+ font-size: 0.9rem;
65
+ color: var(--muted-text-color);
66
+ margin-left: 5px;
67
+ }
68
+
69
+ .dashboard-container {
70
+ display: grid;
71
+ grid-template-columns: repeat(auto-fit, minmax(450px, 1fr)); /* Responsive grid */
72
+ gap: 25px; /* Space between plots */
73
  }
74
  .plot-container {
75
+ background-color: var(--card-bg-color);
76
+ padding: 20px;
77
  border-radius: 8px;
78
+ box-shadow: 0 4px 8px var(--shadow-color);
79
+ border: 1px solid var(--border-color);
80
+ min-height: 450px; /* Ensure plots have some height */
81
+ display: flex; /* For centering loading/error inside */
82
+ justify-content: center;
83
+ align-items: center;
84
+ }
85
+ /* Style Plotly chart container */
86
+ .plot-container .plotly {
87
+ width: 100%;
88
+ height: 100%;
89
  }
90
+
91
  #loading, #error {
92
+ grid-column: 1 / -1; /* Span full width if grid is active */
93
  text-align: center;
94
  font-size: 1.2em;
95
+ padding: 40px;
96
+ color: var(--muted-text-color);
97
  }
98
  #error {
99
+ color: #dc3545; /* Bootstrap danger color */
100
+ font-weight: 500;
101
+ background-color: #f8d7da;
102
+ border: 1px solid #f5c2c7;
103
+ border-radius: 8px;
104
+ }
105
+ footer {
106
+ text-align: center;
107
+ margin-top: 40px;
108
+ padding: 20px;
109
+ font-size: 0.9em;
110
+ color: var(--muted-text-color);
111
+ border-top: 1px solid var(--border-color);
112
+ }
113
+ footer a {
114
+ color: var(--primary-color);
115
+ text-decoration: none;
116
+ }
117
+ footer a:hover {
118
+ text-decoration: underline;
119
  }
120
  </style>
121
  </head>
122
  <body>
123
 
124
+ <div class="container">
125
+ <h1>Provider Inference Metrics Dashboard</h1>
126
+
127
+ <div id="loading">Loading data... Please wait.</div>
128
+ <div id="error" style="display: none;"></div>
129
+
130
+ <!-- KPI Section -->
131
+ <div class="kpi-container" style="display: none;">
132
+ <div class="kpi-card">
133
+ <h3>Total Requests</h3>
134
+ <div class="value" id="kpi-total-requests">--</div>
135
+ </div>
136
+ <div class="kpi-card">
137
+ <h3>Success Rate</h3>
138
+ <div class="value" id="kpi-success-rate">--<span class="unit">%</span></div>
139
+ </div>
140
+ <div class="kpi-card">
141
+ <h3>Avg. Latency (Overall)</h3>
142
+ <div class="value" id="kpi-avg-latency">--<span class="unit">ms</span></div>
143
+ </div>
144
+ <div class="kpi-card">
145
+ <h3>Fastest Provider (Median)</h3>
146
+ <div class="value" id="kpi-fastest-provider">--</div>
147
+ </div>
148
+ </div>
149
+
150
+ <!-- Dashboard Plots -->
151
+ <div class="dashboard-container" style="display: none;">
152
+ <div id="plotLatencyProvider" class="plot-container"></div>
153
+ <div id="plotReliabilityProvider" class="plot-container"></div>
154
+ <div id="plotLatencyModel" class="plot-container"></div>
155
+ <div id="plotErrorTypesProvider" class="plot-container"></div>
156
+ <div id="plotLatencyHeatmap" class="plot-container"></div>
157
+ <!-- Add more divs here for additional plots -->
158
+ </div>
159
+
160
+ <footer id="footer" style="display: none;">
161
+ Data fetched from: <a id="data-source-url" href="#" target="_blank">Hugging Face Datasets</a><br>
162
+ Last updated: <span id="last-updated"></span>
163
+ </footer>
164
  </div>
165
 
166
  <script>
167
  document.addEventListener('DOMContentLoaded', function() {
168
+ const apiUrl = "https://datasets-server.huggingface.co/rows?dataset=victor%2Fproviders-metrics&config=default&split=train&offset=0&length=1000"; // Fetch 1000 rows
169
  const loadingDiv = document.getElementById('loading');
170
  const errorDiv = document.getElementById('error');
171
+ const kpiContainer = document.querySelector('.kpi-container');
172
  const dashboardContainer = document.querySelector('.dashboard-container');
173
+ const footer = document.getElementById('footer');
174
+ const dataSourceUrlElement = document.getElementById('data-source-url');
175
+ const lastUpdatedElement = document.getElementById('last-updated');
176
+
177
+ dataSourceUrlElement.href = apiUrl; // Set link href
178
+
179
+ // Plotly layout defaults
180
+ const baseLayout = {
181
+ margin: { l: 60, r: 30, b: 100, t: 60, pad: 4 }, // Default margins
182
+ legend: { bgcolor: 'rgba(255,255,255,0.5)', bordercolor: '#ccc', borderwidth: 1 },
183
+ colorway: ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'], // Plotly default color sequence
184
+ paper_bgcolor: 'rgba(0,0,0,0)', // Transparent background for plot area
185
+ plot_bgcolor: 'rgba(0,0,0,0)', // Transparent background for plotting area
186
+ font: {
187
+ family: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
188
+ color: '#212529'
189
+ },
190
+ title: {
191
+ font: { size: 16, weight: '500' },
192
+ x: 0.05, // Title alignment
193
+ xanchor: 'left'
194
+ },
195
+ xaxis: {
196
+ gridcolor: '#e9ecef', // Lighter grid lines
197
+ linecolor: '#adb5bd',
198
+ automargin: true,
199
+ tickfont: { size: 10 }
200
+ },
201
+ yaxis: {
202
+ gridcolor: '#e9ecef',
203
+ linecolor: '#adb5bd',
204
+ automargin: true,
205
+ tickfont: { size: 10 }
206
+ }
207
+ };
208
+
209
+ // Helper function to deep merge layout options
210
+ function mergeLayout(customLayout) {
211
+ // Simple deep merge for nested objects like margin, font, title
212
+ let layout = JSON.parse(JSON.stringify(baseLayout)); // Deep copy base
213
+ for (const key in customLayout) {
214
+ if (typeof customLayout[key] === 'object' && customLayout[key] !== null && !Array.isArray(customLayout[key]) && layout[key]) {
215
+ Object.assign(layout[key], customLayout[key]);
216
+ } else {
217
+ layout[key] = customLayout[key];
218
+ }
219
+ }
220
+ return layout;
221
+ }
222
+
223
+ // Helper function to calculate median
224
+ function calculateMedian(arr) {
225
+ if (!arr || arr.length === 0) return null;
226
+ const sortedArr = [...arr].sort((a, b) => a - b);
227
+ const mid = Math.floor(sortedArr.length / 2);
228
+ return sortedArr.length % 2 !== 0 ? sortedArr[mid] : (sortedArr[mid - 1] + sortedArr[mid]) / 2;
229
+ }
230
 
 
 
231
 
232
  fetch(apiUrl)
233
  .then(response => {
234
  if (!response.ok) {
235
+ throw new Error(`HTTP error! status: ${response.status} ${response.statusText}`);
236
  }
237
  return response.json();
238
  })
239
  .then(data => {
240
  loadingDiv.style.display = 'none'; // Hide loading message
241
+ kpiContainer.style.display = 'grid'; // Show KPIs
242
+ dashboardContainer.style.display = 'grid'; // Show dashboard plots
243
+ footer.style.display = 'block'; // Show footer
244
 
245
  // Extract the actual row data
246
  const rows = data.rows.map(item => item.row);
247
  console.log(`Fetched ${rows.length} rows.`);
248
+ lastUpdatedElement.textContent = new Date().toLocaleString();
249
 
250
+ // --- Calculate and Display KPIs ---
251
+ calculateAndDisplayKPIs(rows);
252
 
253
+ // --- Data Processing and Plotting ---
254
  createLatencyByProviderPlot(rows);
 
 
255
  createReliabilityByProviderPlot(rows);
 
 
256
  createLatencyByModelPlot(rows);
 
 
257
  createErrorTypesByProviderPlot(rows);
 
 
258
  createLatencyHeatmap(rows);
259
 
260
  })
261
  .catch(error => {
262
  console.error('Error fetching or processing data:', error);
263
  loadingDiv.style.display = 'none';
264
+ errorDiv.textContent = `Error loading data: ${error.message}. Please check the console for details. Is the dataset server reachable?`;
265
  errorDiv.style.display = 'block';
266
  });
267
 
268
+ // --- KPI Calculation Function ---
269
+ function calculateAndDisplayKPIs(rows) {
270
+ const totalRequests = rows.length;
271
+ const successfulRequests = rows.filter(r => r.response_status_code === 200).length;
272
+ const successRate = totalRequests > 0 ? ((successfulRequests / totalRequests) * 100).toFixed(1) : 0;
273
+
274
+ const validLatencies = rows
275
+ .map(r => r.duration_ms)
276
+ .filter(d => d !== null && d >= 0);
277
+ const avgLatency = validLatencies.length > 0
278
+ ? (validLatencies.reduce((a, b) => a + b, 0) / validLatencies.length).toFixed(0)
279
+ : 0;
280
+
281
+ // Calculate median latency per provider to find the fastest
282
+ const latencyByProvider = {};
283
+ rows.forEach(row => {
284
+ if (row.duration_ms !== null && row.duration_ms >= 0) {
285
+ if (!latencyByProvider[row.provider_name]) {
286
+ latencyByProvider[row.provider_name] = [];
287
+ }
288
+ latencyByProvider[row.provider_name].push(row.duration_ms);
289
+ }
290
+ });
291
+
292
+ let fastestProvider = '--';
293
+ let minMedianLatency = Infinity;
294
+
295
+ for (const provider in latencyByProvider) {
296
+ const median = calculateMedian(latencyByProvider[provider]);
297
+ if (median !== null && median < minMedianLatency) {
298
+ minMedianLatency = median;
299
+ fastestProvider = provider;
300
+ }
301
+ }
302
+
303
+ document.getElementById('kpi-total-requests').textContent = totalRequests;
304
+ document.getElementById('kpi-success-rate').innerHTML = `${successRate}<span class="unit">%</span>`;
305
+ document.getElementById('kpi-avg-latency').innerHTML = `${avgLatency}<span class="unit">ms</span>`;
306
+ document.getElementById('kpi-fastest-provider').textContent = fastestProvider;
307
+ }
308
+
309
+
310
  // --- Plotting Functions ---
311
 
312
  function createLatencyByProviderPlot(rows) {
 
315
  if (!dataByProvider[row.provider_name]) {
316
  dataByProvider[row.provider_name] = [];
317
  }
 
318
  if (row.duration_ms !== null && row.duration_ms >= 0) {
319
  dataByProvider[row.provider_name].push(row.duration_ms);
320
  }
321
  });
322
 
323
+ const plotData = Object.keys(dataByProvider).sort().map(provider => ({ // Sort providers alphabetically
324
  y: dataByProvider[provider],
325
  type: 'box',
326
  name: provider,
327
+ boxpoints: 'Outliers',
328
+ marker: { size: 4 }
329
  }));
330
 
331
+ const layout = mergeLayout({
332
+ title: { text: 'Latency Distribution by Provider' },
333
+ yaxis: { title: 'Duration (ms)', type: 'log', autorange: true },
334
+ xaxis: { title: 'Provider', tickangle: -30 },
335
+ margin: { b: 120 } // More bottom margin for angled labels
336
+ });
337
 
338
+ Plotly.newPlot('plotLatencyProvider', plotData, layout, {responsive: true});
339
  }
340
 
341
  function createReliabilityByProviderPlot(rows) {
 
345
 
346
  rows.forEach(row => {
347
  const provider = row.provider_name;
348
+ const status = row.response_status_code ?? 'Unknown'; // Handle null status
349
  allProviders.add(provider);
350
  allStatusCodes.add(status);
351
 
 
359
  });
360
 
361
  const sortedProviders = Array.from(allProviders).sort();
362
+ // Sort status codes: 200 first, then numerically, then 'Unknown'
363
+ const sortedStatusCodes = Array.from(allStatusCodes).sort((a, b) => {
364
+ if (a === 200) return -1;
365
+ if (b === 200) return 1;
366
+ if (a === 'Unknown') return 1;
367
+ if (b === 'Unknown') return -1;
368
+ return a - b;
369
+ });
370
 
371
  const plotData = sortedStatusCodes.map(status => {
372
  return {
373
  x: sortedProviders,
374
  y: sortedProviders.map(provider => statusCountsByProvider[provider]?.[status] || 0),
375
  name: `Status ${status}`,
376
+ type: 'bar',
377
+ hovertemplate: `Provider: %{x}<br>Status: ${status}<br>Count: %{y}<extra></extra>`
378
  };
379
  });
380
 
381
+ const layout = mergeLayout({
382
+ title: { text: 'Request Status Codes by Provider' },
383
  barmode: 'stack',
384
+ xaxis: { title: 'Provider', tickangle: -30 },
385
+ yaxis: { title: 'Number of Requests', autorange: true },
386
+ margin: { b: 120 }
387
+ });
388
 
389
+ Plotly.newPlot('plotReliabilityProvider', plotData, layout, {responsive: true});
390
  }
391
 
392
  function createLatencyByModelPlot(rows) {
 
396
  if (!dataByModel[model]) {
397
  dataByModel[model] = [];
398
  }
 
399
  if (row.duration_ms !== null && row.duration_ms >= 0) {
400
  dataByModel[model].push(row.duration_ms);
401
  }
402
  });
403
 
404
+ const plotData = Object.keys(dataByModel).sort().map(model => ({ // Sort models
405
  y: dataByModel[model],
406
  type: 'box',
407
  name: model,
408
+ boxpoints: 'Outliers',
409
+ marker: { size: 4 }
410
  }));
411
 
412
+ const layout = mergeLayout({
413
+ title: { text: 'Latency Distribution by Model' },
414
+ yaxis: { title: 'Duration (ms)', type: 'log', autorange: true },
415
  xaxis: {
416
  title: 'Model ID',
417
+ tickangle: -30 // Angle labels if they overlap
418
  },
419
+ margin: { b: 180 } // More bottom margin for potentially long/angled labels
420
+ });
421
 
422
+ Plotly.newPlot('plotLatencyModel', plotData, layout, {responsive: true});
423
  }
424
 
425
  function createErrorTypesByProviderPlot(rows) {
 
452
  x: sortedProviders,
453
  y: sortedProviders.map(provider => errorCountsByProvider[provider]?.[status] || 0),
454
  name: `Error ${status}`,
455
+ type: 'bar',
456
+ hovertemplate: `Provider: %{x}<br>Error: ${status}<br>Count: %{y}<extra></extra>`
457
  };
458
  });
459
 
460
+ const layout = mergeLayout({
461
+ title: { text: 'Error Types by Provider (Non-200 Status)' },
462
  barmode: 'group', // Group bars side-by-side for comparison
463
+ xaxis: { title: 'Provider', tickangle: -30 },
464
+ yaxis: { title: 'Number of Errors', autorange: true },
465
+ margin: { b: 120 }
466
+ });
467
 
468
+ Plotly.newPlot('plotErrorTypesProvider', plotData, layout, {responsive: true});
469
  }
470
 
471
  function createLatencyHeatmap(rows) {
 
494
  const zValues = sortedModels.map(model => {
495
  return sortedProviders.map(provider => {
496
  const data = latencyData[provider]?.[model];
497
+ const avg = data && data.count > 0 ? data.sum / data.count : null;
498
+ return avg;
499
  });
500
  });
501
 
502
+ const hoverText = sortedModels.map(model => {
503
+ return sortedProviders.map(provider => {
504
+ const data = latencyData[provider]?.[model];
505
+ const avg = data && data.count > 0 ? (data.sum / data.count).toFixed(0) : 'N/A';
506
+ const count = data?.count || 0;
507
+ return `Model: ${model}<br>Provider: ${provider}<br>Avg Latency: ${avg} ms<br>Requests: ${count}<extra></extra>`;
508
+ });
509
+ });
510
+
511
+
512
  const plotData = [{
513
  z: zValues,
514
  x: sortedProviders,
515
  y: sortedModels,
516
  type: 'heatmap',
517
+ hoverongaps: false,
518
+ colorscale: 'Viridis',
519
+ reversescale: true, // Often makes sense for latency (lower is better -> brighter)
520
+ colorbar: { title: 'Avg Latency (ms)', titleside: 'right', thickness: 15 },
521
+ xgap: 2, // Add gaps between cells
522
+ ygap: 2,
523
+ hovertemplate: hoverText // Custom hover text
524
  }];
525
 
526
+ const layout = mergeLayout({
527
+ title: { text: 'Average Latency (ms) - Model vs. Provider' },
528
+ xaxis: { title: '', side: 'top', tickangle: -30 }, // Move x-axis labels to top, angle
529
+ yaxis: { title: '', autorange: 'reversed' }, // Reverse y-axis to match typical matrix layout
530
+ margin: { l: 280, r: 50, b: 50, t: 120 } // Adjust margins significantly for labels
531
+ });
532
 
533
+ Plotly.newPlot('plotLatencyHeatmap', plotData, layout, {responsive: true});
534
  }
535
 
536
  });