gradia 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,312 @@
1
+ :root {
2
+ --bg-primary: #343541;
3
+ --bg-secondary: #202123;
4
+ --bg-card: #444654;
5
+ --text-primary: #ececf1;
6
+ --text-secondary: #c5c5d2;
7
+ --accent: #10a37f;
8
+ /* ChatGPT Green */
9
+ --accent-glow: rgba(16, 163, 127, 0.2);
10
+ --success: #10a37f;
11
+ --border: #565869;
12
+ --font-main: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
13
+ }
14
+
15
+ body {
16
+ margin: 0;
17
+ padding: 0;
18
+ font-family: var(--font-main);
19
+ background-color: var(--bg-primary);
20
+ color: var(--text-primary);
21
+ overflow-x: hidden;
22
+ }
23
+
24
+ .layout {
25
+ display: grid;
26
+ grid-template-columns: 260px 1fr;
27
+ height: 100vh;
28
+ overflow: hidden;
29
+ }
30
+
31
+ /* Sidebar */
32
+ .sidebar {
33
+ background-color: var(--bg-secondary);
34
+ padding: 15px;
35
+ display: flex;
36
+ flex-direction: column;
37
+ height: 100%;
38
+ border-right: 1px solid rgba(255, 255, 255, 0.1);
39
+ }
40
+
41
+ .sidebar .brand-logo {
42
+ height: 40px;
43
+ margin-bottom: 20px;
44
+ margin-left: 10px;
45
+ }
46
+
47
+ .sidebar-section {
48
+ margin-bottom: 30px;
49
+ padding: 0 10px;
50
+ }
51
+
52
+ .sidebar-label {
53
+ font-size: 0.75rem;
54
+ color: var(--text-secondary);
55
+ text-transform: uppercase;
56
+ font-weight: 600;
57
+ margin-bottom: 10px;
58
+ letter-spacing: 0.5px;
59
+ }
60
+
61
+ /* Main Content */
62
+ .main {
63
+ padding: 40px 60px;
64
+ overflow-y: auto;
65
+ background-color: var(--bg-primary);
66
+ position: relative;
67
+ max-width: 1200px;
68
+ margin: 0 auto;
69
+ width: 100%;
70
+ }
71
+
72
+ .grid {
73
+ display: grid;
74
+ grid-template-columns: repeat(2, 1fr);
75
+ gap: 20px;
76
+ }
77
+
78
+ /* Wide charts should span full width */
79
+ .col-full {
80
+ grid-column: span 2;
81
+ }
82
+
83
+ .card {
84
+ background: var(--bg-card);
85
+ border: 1px solid rgba(255, 255, 255, 0.05);
86
+ border-radius: 8px;
87
+ padding: 20px;
88
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
89
+ transition: none;
90
+ /* Flat design doesn't hover much */
91
+ }
92
+
93
+ .card h3 {
94
+ margin-top: 0;
95
+ margin-bottom: 15px;
96
+ font-size: 1rem;
97
+ color: var(--text-primary);
98
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
99
+ padding-bottom: 10px;
100
+ }
101
+
102
+ canvas {
103
+ width: 100% !important;
104
+ height: 250px !important;
105
+ }
106
+
107
+ /* Wizard overrides for consistency */
108
+ .container-center {
109
+ background: var(--bg-primary);
110
+ }
111
+
112
+ .wizard-card {
113
+ background: var(--bg-secondary);
114
+ border: 1px solid var(--border);
115
+ border-radius: 8px;
116
+ }
117
+
118
+ /* Brand */
119
+ .brand-logo {
120
+ height: 80px;
121
+ margin-bottom: 25px;
122
+ }
123
+
124
+ .subtitle {
125
+ color: var(--text-secondary);
126
+ text-align: center;
127
+ margin-top: 0px;
128
+ margin-bottom: 25px;
129
+ font-size: 0.9rem;
130
+ }
131
+
132
+ .footer {
133
+ display: flex;
134
+ flex-direction: column;
135
+ align-items: center;
136
+ margin-top: 50px;
137
+ padding-top: 20px;
138
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
139
+ }
140
+
141
+ .footer-text {
142
+ font-size: 0.8rem;
143
+ color: #8e8ea0;
144
+ margin-bottom: 10px;
145
+ }
146
+
147
+ .social-links {
148
+ display: flex;
149
+ gap: 20px;
150
+ }
151
+
152
+ .social-icon {
153
+ width: 20px;
154
+ height: 20px;
155
+ fill: #8e8ea0;
156
+ transition: fill 0.2s;
157
+ }
158
+
159
+ .social-icon:hover {
160
+ fill: var(--text-primary);
161
+ }
162
+
163
+ .description {
164
+ font-size: 0.75rem;
165
+ color: #8b949e;
166
+ font-weight: 400;
167
+ margin-top: 4px;
168
+ display: block;
169
+ }
170
+
171
+ .dual-input {
172
+ display: flex;
173
+ gap: 15px;
174
+ align-items: center;
175
+ }
176
+
177
+ .dual-input input[type=number] {
178
+ width: 80px;
179
+ background: #0d1117;
180
+ border: 1px solid #30363d;
181
+ border-radius: 6px;
182
+ color: #fff;
183
+ padding: 8px;
184
+ }
185
+
186
+ .logo-large {
187
+ font-size: 3rem;
188
+ font-weight: 800;
189
+ margin-bottom: 20px;
190
+ background: linear-gradient(90deg, #58a6ff, #a371f7);
191
+ -webkit-background-clip: text;
192
+ -webkit-text-fill-color: transparent;
193
+ }
194
+
195
+ .logo-large span {
196
+ font-size: 1rem;
197
+ color: var(--text-secondary);
198
+ -webkit-text-fill-color: var(--text-secondary);
199
+ font-weight: 400;
200
+ }
201
+
202
+ .wizard-card {
203
+ width: 100%;
204
+ max-width: 500px;
205
+ padding: 40px;
206
+ background: #161b22;
207
+ border: 1px solid #30363d;
208
+ }
209
+
210
+ .scenario-badge {
211
+ background: rgba(88, 166, 255, 0.1);
212
+ color: #58a6ff;
213
+ padding: 10px;
214
+ border-radius: 6px;
215
+ margin-bottom: 30px;
216
+ text-align: center;
217
+ font-size: 0.9rem;
218
+ }
219
+
220
+ .highlight {
221
+ font-weight: 700;
222
+ color: #fff;
223
+ }
224
+
225
+ .form-group {
226
+ margin-bottom: 25px;
227
+ }
228
+
229
+ label {
230
+ display: block;
231
+ margin-bottom: 10px;
232
+ color: #f0f6fc;
233
+ font-size: 0.95rem;
234
+ font-weight: 600;
235
+ }
236
+
237
+ .val-badge {
238
+ background: #30363d;
239
+ padding: 2px 8px;
240
+ border-radius: 10px;
241
+ color: #fff;
242
+ font-size: 0.8rem;
243
+ }
244
+
245
+ /* Form Controls */
246
+ .select-wrapper select {
247
+ width: 100%;
248
+ padding: 12px;
249
+ background: #0d1117;
250
+ border: 1px solid #30363d;
251
+ border-radius: 6px;
252
+ color: #f0f6fc;
253
+ font-family: inherit;
254
+ font-size: 1rem;
255
+ outline: none;
256
+ cursor: pointer;
257
+ }
258
+
259
+ .select-wrapper select:focus {
260
+ border-color: var(--accent);
261
+ }
262
+
263
+ input[type=range] {
264
+ width: 100%;
265
+ background: transparent;
266
+ -webkit-appearance: none;
267
+ }
268
+
269
+ input[type=range]::-webkit-slider-thumb {
270
+ -webkit-appearance: none;
271
+ height: 16px;
272
+ width: 16px;
273
+ border-radius: 50%;
274
+ background: var(--accent);
275
+ cursor: pointer;
276
+ margin-top: -6px;
277
+ }
278
+
279
+ input[type=range]::-webkit-slider-runnable-track {
280
+ width: 100%;
281
+ height: 4px;
282
+ background: #30363d;
283
+ border-radius: 2px;
284
+ }
285
+
286
+ .btn-primary {
287
+ width: 100%;
288
+ padding: 15px;
289
+ background: var(--success);
290
+ color: white;
291
+ border: none;
292
+ border-radius: 6px;
293
+ font-size: 1rem;
294
+ font-weight: 700;
295
+ cursor: pointer;
296
+ transition: background 0.2s;
297
+ }
298
+
299
+ .btn-primary:hover {
300
+ background: #2ea043;
301
+ }
302
+
303
+ .btn-primary:disabled {
304
+ background: #30363d;
305
+ cursor: not-allowed;
306
+ }
307
+
308
+ .sub-label {
309
+ font-size: 0.75rem;
310
+ color: #484f58;
311
+ margin-bottom: 8px;
312
+ }
@@ -0,0 +1,348 @@
1
+ // app.js
2
+
3
+ const state = {
4
+ events: [],
5
+ lastIdx: 0,
6
+ charts: {},
7
+ scenario: null,
8
+ totalEpochs: 0
9
+ };
10
+
11
+ async function fetchEvents() {
12
+ try {
13
+ const res = await fetch('/api/events');
14
+ const allEvents = await res.json();
15
+
16
+ // Process new events
17
+ if (allEvents.length > state.lastIdx) {
18
+ const newEvents = allEvents.slice(state.lastIdx);
19
+ newEvents.forEach(handleEvent);
20
+ state.lastIdx = allEvents.length;
21
+ }
22
+ } catch (e) {
23
+ console.error("Failed to fetch events", e);
24
+ }
25
+ }
26
+
27
+ function handleEvent(event) {
28
+ console.log("Event:", event.type, event.data);
29
+
30
+ try {
31
+ switch (event.type) {
32
+ case 'train_begin':
33
+ document.getElementById('status-text').innerText = "Training";
34
+ state.scenario = event.data;
35
+ state.totalEpochs = event.data.epochs || 20; // Default fallback
36
+ // Init Progress
37
+ updateProgress(0);
38
+
39
+ renderScenarioInfo(event.data);
40
+ initCharts(event.data);
41
+ break;
42
+
43
+ case 'epoch_end':
44
+ ensureCharts(); // Robustness
45
+ updateCharts(event.data);
46
+ // document.getElementById('epoch-text').innerText = event.data.epoch; // Removed in favor of bar
47
+ updateMetricsTable(event.data);
48
+ updateProgress(event.data.epoch);
49
+ break;
50
+
51
+ case 'system_metrics':
52
+ ensureCharts(); // Robustness
53
+ updateSystemCharts(event.data);
54
+ break;
55
+
56
+ case 'train_end':
57
+ document.getElementById('status-text').innerText = "Completed";
58
+ updateProgress(state.totalEpochs); // Ensure full feature
59
+ document.getElementById('status-text').style.color = "var(--success)";
60
+ if (event.data.feature_importance) {
61
+ updateFeatureImportance(event.data.feature_importance);
62
+ }
63
+ break;
64
+ }
65
+ } catch (err) {
66
+ console.error("Error handling event " + event.type, err);
67
+ }
68
+ }
69
+
70
+ function ensureCharts() {
71
+ if (!state.charts.metric) {
72
+ // Fallback init if train_begin missed
73
+ initCharts({ features: [], target_column: 'Unknown' });
74
+ }
75
+ }
76
+
77
+ function renderScenarioInfo(data) {
78
+ const el = document.getElementById('scenario-details');
79
+ // data.scenario is a string repr, or we use individual fields
80
+ el.innerHTML = `
81
+ Target: <span style="color:white">${data.features ? 'Defined' : 'Auto'}</span><br>
82
+ Features: <span style="color:white">${data.features ? data.features.length : 0}</span>
83
+ `;
84
+ }
85
+
86
+ function initCharts(data) {
87
+ // Metric Chart
88
+ const ctx = document.getElementById('metricChart').getContext('2d');
89
+ state.charts.metric = new Chart(ctx, {
90
+ type: 'line',
91
+ data: {
92
+ labels: [],
93
+ datasets: [
94
+ { label: 'Train', data: [], borderColor: '#58a6ff', tension: 0.3 },
95
+ { label: 'Test', data: [], borderColor: '#a371f7', tension: 0.3 }
96
+ ]
97
+ },
98
+ options: {
99
+ responsive: true, maintainAspectRatio: false, animation: false,
100
+ scales: { y: { grid: { color: '#30363d' } }, x: { grid: { color: '#30363d' } } },
101
+ plugins: { legend: { labels: { color: '#8b949e' } } }
102
+ }
103
+ });
104
+
105
+ // System Chart (Gradient Area)
106
+ const ctxSys = document.getElementById('systemChart').getContext('2d');
107
+
108
+ // Gradients
109
+ const gradCpu = ctxSys.createLinearGradient(0, 0, 0, 400);
110
+ gradCpu.addColorStop(0, 'rgba(35, 134, 54, 0.5)');
111
+ gradCpu.addColorStop(1, 'rgba(35, 134, 54, 0.0)');
112
+
113
+ const gradRam = ctxSys.createLinearGradient(0, 0, 0, 400);
114
+ gradRam.addColorStop(0, 'rgba(247, 129, 102, 0.5)');
115
+ gradRam.addColorStop(1, 'rgba(247, 129, 102, 0.0)');
116
+
117
+ state.charts.system = new Chart(ctxSys, {
118
+ type: 'line',
119
+ data: {
120
+ labels: [],
121
+ datasets: [
122
+ {
123
+ label: 'CPU %',
124
+ data: [],
125
+ borderColor: '#238636',
126
+ backgroundColor: gradCpu,
127
+ fill: true,
128
+ tension: 0.4,
129
+ pointRadius: 0
130
+ },
131
+ {
132
+ label: 'RAM %',
133
+ data: [],
134
+ borderColor: '#f78166',
135
+ backgroundColor: gradRam,
136
+ fill: true,
137
+ tension: 0.4,
138
+ pointRadius: 0
139
+ }
140
+ ]
141
+ },
142
+ options: {
143
+ responsive: true, maintainAspectRatio: false, animation: false,
144
+ scales: {
145
+ y: { min: 0, max: 100, grid: { color: '#30363d' } },
146
+ x: { display: false }
147
+ }
148
+ }
149
+ });
150
+
151
+ // FI Chart
152
+ const ctxFi = document.getElementById('fiChart').getContext('2d');
153
+ state.charts.fi = new Chart(ctxFi, {
154
+ type: 'bar',
155
+ data: {
156
+ labels: [],
157
+ datasets: [{
158
+ label: 'Importance',
159
+ data: [],
160
+ backgroundColor: '#238636',
161
+ borderRadius: 4
162
+ }]
163
+ },
164
+ options: {
165
+ responsive: true, maintainAspectRatio: false, indexAxis: 'y',
166
+ scales: { y: { grid: { display: false }, ticks: { color: '#8b949e' } }, x: { grid: { color: '#30363d' } } },
167
+ plugins: { legend: { display: false } }
168
+ }
169
+ });
170
+ }
171
+
172
+ function updateCharts(data) {
173
+ if (!state.charts.metric) return;
174
+
175
+ // Check which metrics to plot
176
+ let trainVal = data.train_acc || data.train_mse;
177
+ let testVal = data.test_acc || data.test_mse;
178
+
179
+ const chart = state.charts.metric;
180
+ chart.data.labels.push(data.epoch || 1);
181
+ chart.data.datasets[0].data.push(trainVal);
182
+ chart.data.datasets[1].data.push(testVal);
183
+
184
+ // Use proper labels
185
+ if (data.train_acc) {
186
+ chart.data.datasets[0].label = "Train Accuracy";
187
+ chart.data.datasets[1].label = "Test Accuracy";
188
+ } else {
189
+ chart.data.datasets[0].label = "Train MSE";
190
+ chart.data.datasets[1].label = "Test MSE";
191
+ }
192
+
193
+ chart.update();
194
+ }
195
+
196
+ function updateSystemCharts(data) {
197
+ if (!state.charts.system) return;
198
+
199
+ const chart = state.charts.system;
200
+ // Keep max 60 points
201
+ if (chart.data.labels.length > 60) {
202
+ chart.data.labels.shift();
203
+ chart.data.datasets[0].data.shift();
204
+ chart.data.datasets[1].data.shift();
205
+ }
206
+
207
+ chart.data.labels.push(""); // timestamp irrelevant for sparkline feel
208
+ chart.data.datasets[0].data.push(data.cpu);
209
+ chart.data.datasets[1].data.push(data.ram);
210
+ chart.update();
211
+ }
212
+
213
+ function updateFeatureImportance(fiMap) {
214
+ if (!state.charts.fi || !fiMap) return;
215
+
216
+ const labels = Object.keys(fiMap);
217
+ const values = Object.values(fiMap);
218
+
219
+ state.charts.fi.data.labels = labels;
220
+ state.charts.fi.data.datasets[0].data = values;
221
+ state.charts.fi.update();
222
+ }
223
+
224
+ function updateMetricsTable(data) {
225
+ const el = document.getElementById('metrics-table');
226
+ let html = "";
227
+
228
+ // Priority metrics first
229
+ const priority = ['train_acc', 'test_acc', 'f1', 'precision', 'recall', 'train_mse', 'test_mse', 'mae', 'r2'];
230
+
231
+ // Process priority first
232
+ for (const key of priority) {
233
+ if (data[key] !== undefined) {
234
+ html += renderMetricRow(key, data[key]);
235
+ }
236
+ }
237
+
238
+ // Rest
239
+ for (const [k, v] of Object.entries(data)) {
240
+ if (typeof v === 'number' && k !== 'epoch' && !priority.includes(k)) {
241
+ html += renderMetricRow(k, v);
242
+ }
243
+ }
244
+ el.innerHTML = html;
245
+ }
246
+
247
+ function renderMetricRow(k, v) {
248
+ // Pretty names
249
+ const names = {
250
+ 'train_acc': 'Train Accuracy', 'test_acc': 'Test Accuracy',
251
+ 'train_mse': 'Train MSE', 'test_mse': 'Test MSE',
252
+ 'f1': 'F1 Score', 'precision': 'Precision', 'recall': 'Recall',
253
+ 'mae': 'MAE', 'r2': 'R² Score'
254
+ };
255
+
256
+ const label = names[k] || k;
257
+ const color = k.startsWith('test') || k === 'f1' ? 'var(--accent)' : 'var(--text-secondary)';
258
+
259
+ let valStr = String(v);
260
+ if (typeof v === 'number') {
261
+ valStr = v.toFixed(4);
262
+ } else if (v === null || v === undefined) {
263
+ valStr = "-";
264
+ }
265
+
266
+ return `<div style="display:flex; justify-content:space-between; margin-bottom:8px; border-bottom:1px solid #202020; padding-bottom:4px">
267
+ <span style="color:${color}; font-size:0.85rem">${label}</span>
268
+ <span style="font-family:monospace; font-weight:600">${valStr}</span>
269
+ </div>`;
270
+ }
271
+
272
+ function updateProgress(current) {
273
+ const total = state.totalEpochs;
274
+ const pct = Math.min((current / total) * 100, 100);
275
+
276
+ document.getElementById('progress-fill').style.width = `${pct}%`;
277
+ document.getElementById('progress-text').innerText = `${current} / ${total} Epochs`;
278
+ }
279
+
280
+ // ... (polling)
281
+ let pollInterval = setInterval(fetchEvents, 500);
282
+ fetchEvents();
283
+
284
+ /* --- New Actions --- */
285
+
286
+ function downloadReport(type) {
287
+ if (type === 'json') {
288
+ window.open('/api/report/json', '_blank');
289
+ } else {
290
+ window.open('/api/report/pdf', '_blank');
291
+ }
292
+ }
293
+
294
+ async function runEvaluation() {
295
+ const btn = document.querySelector('button[onclick="runEvaluation()"]');
296
+ btn.innerText = "Running...";
297
+ btn.disabled = true;
298
+
299
+ try {
300
+ const res = await fetch('/api/evaluate', { method: 'POST' });
301
+ const data = await res.json();
302
+
303
+ if (data.confusion_matrix) {
304
+ renderConfusionMatrix(data.confusion_matrix, data.classes);
305
+ } else {
306
+ alert("Evaluation passed, but only classification supports Matrix currently.");
307
+ }
308
+ } catch (e) {
309
+ alert("Evaluation failed: " + e);
310
+ } finally {
311
+ btn.innerText = "Run Evaluation";
312
+ btn.disabled = false;
313
+ }
314
+ }
315
+
316
+ function renderConfusionMatrix(matrix, classes) {
317
+ document.getElementById('eval-card').style.display = 'block';
318
+ const container = document.getElementById('cm-container');
319
+
320
+ // Simple HTML heatmap
321
+ let html = '<table style="border-collapse: collapse; font-size: 0.8rem;">';
322
+
323
+ // Header
324
+ html += '<tr><td></td>';
325
+ classes.forEach(c => html += `<td style="padding:5px; font-weight:bold; color:var(--text-secondary)">${c}</td>`);
326
+ html += '</tr>';
327
+
328
+ // Find max for color scaling
329
+ let maxVal = 0;
330
+ matrix.forEach(row => row.forEach(v => maxVal = Math.max(maxVal, v)));
331
+
332
+ matrix.forEach((row, i) => {
333
+ html += `<tr><td style="padding:5px; font-weight:bold; color:var(--text-secondary)">${classes[i]}</td>`;
334
+ row.forEach(val => {
335
+ const intensity = maxVal > 0 ? val / maxVal : 0;
336
+ // Green intensity
337
+ const bg = `rgba(16, 163, 127, ${intensity * 0.8 + 0.1})`;
338
+ html += `<td style="background:${bg}; padding:10px 15px; text-align:center; border:1px solid #333; color:white;">${val}</td>`;
339
+ });
340
+ html += '</tr>';
341
+ });
342
+
343
+ html += '</table>';
344
+ container.innerHTML = html;
345
+
346
+ // Scroll to it
347
+ document.getElementById('eval-card').scrollIntoView({ behavior: 'smooth' });
348
+ }