claude-code-templates 1.12.2 → 1.13.0
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.
- package/package.json +1 -1
- package/src/analytics/core/AgentAnalyzer.js +341 -0
- package/src/analytics/core/SessionAnalyzer.js +101 -46
- package/src/analytics-web/components/AgentAnalytics.js +710 -0
- package/src/analytics-web/components/DashboardPage.js +810 -39
- package/src/analytics-web/components/SessionTimer.js +14 -11
- package/src/analytics-web/index.html +666 -65
- package/src/analytics.js +112 -10
- package/src/analytics-web/components/Dashboard.js.deprecated +0 -589
|
@@ -188,13 +188,9 @@ class DashboardPage {
|
|
|
188
188
|
<div class="metrics-cards-container">
|
|
189
189
|
<!-- Conversations Card -->
|
|
190
190
|
<div class="metric-card">
|
|
191
|
-
<div class="metric-header">
|
|
192
|
-
<div class="metric-icon">💬</div>
|
|
193
|
-
<div class="metric-title">Conversations</div>
|
|
194
|
-
</div>
|
|
195
191
|
<div class="metric-primary">
|
|
196
192
|
<span class="metric-primary-value" id="totalConversations">0</span>
|
|
197
|
-
<span class="metric-primary-label">Total</span>
|
|
193
|
+
<span class="metric-primary-label">Total Conversations</span>
|
|
198
194
|
</div>
|
|
199
195
|
<div class="metric-secondary">
|
|
200
196
|
<div class="metric-secondary-item">
|
|
@@ -214,13 +210,9 @@ class DashboardPage {
|
|
|
214
210
|
|
|
215
211
|
<!-- Sessions Card -->
|
|
216
212
|
<div class="metric-card">
|
|
217
|
-
<div class="metric-header">
|
|
218
|
-
<div class="metric-icon">⚡</div>
|
|
219
|
-
<div class="metric-title">Sessions</div>
|
|
220
|
-
</div>
|
|
221
213
|
<div class="metric-primary">
|
|
222
214
|
<span class="metric-primary-value" id="claudeSessions">0</span>
|
|
223
|
-
<span class="metric-primary-label">Total</span>
|
|
215
|
+
<span class="metric-primary-label">Total Sessions</span>
|
|
224
216
|
</div>
|
|
225
217
|
<div class="metric-secondary">
|
|
226
218
|
<div class="metric-secondary-item">
|
|
@@ -240,13 +232,9 @@ class DashboardPage {
|
|
|
240
232
|
|
|
241
233
|
<!-- Tokens Card -->
|
|
242
234
|
<div class="metric-card">
|
|
243
|
-
<div class="metric-header">
|
|
244
|
-
<div class="metric-icon">🔢</div>
|
|
245
|
-
<div class="metric-title">Tokens</div>
|
|
246
|
-
</div>
|
|
247
235
|
<div class="metric-primary">
|
|
248
236
|
<span class="metric-primary-value" id="totalTokens">0</span>
|
|
249
|
-
<span class="metric-primary-label">Total</span>
|
|
237
|
+
<span class="metric-primary-label">Total Tokens</span>
|
|
250
238
|
</div>
|
|
251
239
|
<div class="metric-secondary">
|
|
252
240
|
<div class="metric-secondary-item">
|
|
@@ -263,6 +251,28 @@ class DashboardPage {
|
|
|
263
251
|
</div>
|
|
264
252
|
</div>
|
|
265
253
|
</div>
|
|
254
|
+
|
|
255
|
+
<!-- Agents Card -->
|
|
256
|
+
<div class="metric-card">
|
|
257
|
+
<div class="metric-primary">
|
|
258
|
+
<span class="metric-primary-value" id="totalAgentInvocations">0</span>
|
|
259
|
+
<span class="metric-primary-label">Total Agent Uses</span>
|
|
260
|
+
</div>
|
|
261
|
+
<div class="metric-secondary">
|
|
262
|
+
<div class="metric-secondary-item">
|
|
263
|
+
<span class="metric-secondary-label">Types:</span>
|
|
264
|
+
<span class="metric-secondary-value" id="totalAgentTypes">0</span>
|
|
265
|
+
</div>
|
|
266
|
+
<div class="metric-secondary-item">
|
|
267
|
+
<span class="metric-secondary-label">Top Agent:</span>
|
|
268
|
+
<span class="metric-secondary-value" id="topAgentName">None</span>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="metric-secondary-item">
|
|
271
|
+
<span class="metric-secondary-label">Adoption:</span>
|
|
272
|
+
<span class="metric-secondary-value" id="agentAdoption">0%</span>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
266
276
|
</div>
|
|
267
277
|
|
|
268
278
|
<!-- Session Timer Section -->
|
|
@@ -286,35 +296,96 @@ class DashboardPage {
|
|
|
286
296
|
</div>
|
|
287
297
|
</div>
|
|
288
298
|
|
|
289
|
-
<!-- Charts Container
|
|
299
|
+
<!-- Charts Container - Organized by Sections -->
|
|
290
300
|
<div class="charts-container">
|
|
291
|
-
<div class="chart-card">
|
|
292
|
-
<div class="chart-title">
|
|
293
|
-
📊 token usage over time
|
|
294
|
-
</div>
|
|
295
|
-
<canvas id="tokenChart" class="chart-canvas"></canvas>
|
|
296
|
-
</div>
|
|
297
301
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
302
|
+
<!-- SECTION 1: Token Analytics -->
|
|
303
|
+
<div class="chart-section">
|
|
304
|
+
<div class="section-header">
|
|
305
|
+
<h3 class="section-title">🔢 Token Analytics</h3>
|
|
306
|
+
<p class="section-description">Monitor token consumption patterns and efficiency</p>
|
|
307
|
+
</div>
|
|
308
|
+
<div class="section-charts">
|
|
309
|
+
<div class="chart-card">
|
|
310
|
+
<div class="chart-title">
|
|
311
|
+
Token Usage Over Time
|
|
312
|
+
</div>
|
|
313
|
+
<canvas id="tokenChart" class="chart-canvas"></canvas>
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
<div class="chart-card">
|
|
317
|
+
<div class="chart-title">
|
|
318
|
+
Token Distribution by Type
|
|
319
|
+
</div>
|
|
320
|
+
<canvas id="tokenTypeChart" class="chart-canvas"></canvas>
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<div class="chart-card">
|
|
324
|
+
<div class="chart-title">
|
|
325
|
+
Token Usage Over Time
|
|
326
|
+
</div>
|
|
327
|
+
<canvas id="tokenTimelineChart" class="chart-canvas"></canvas>
|
|
328
|
+
</div>
|
|
301
329
|
</div>
|
|
302
|
-
<canvas id="projectChart" class="chart-canvas"></canvas>
|
|
303
330
|
</div>
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
331
|
+
|
|
332
|
+
<!-- SECTION 2: Workflow Intelligence -->
|
|
333
|
+
<div class="chart-section">
|
|
334
|
+
<div class="section-header">
|
|
335
|
+
<h3 class="section-title">🤖 Workflow Intelligence</h3>
|
|
336
|
+
<p class="section-description">Analyze agent usage and automation patterns</p>
|
|
337
|
+
</div>
|
|
338
|
+
<div class="section-charts">
|
|
339
|
+
<div class="chart-card">
|
|
340
|
+
<div class="chart-title">
|
|
341
|
+
Agent Usage Distribution
|
|
342
|
+
</div>
|
|
343
|
+
<canvas id="agentUsageChart" class="chart-canvas"></canvas>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<div class="chart-card">
|
|
347
|
+
<div class="chart-title">
|
|
348
|
+
Agent Activity Timeline
|
|
349
|
+
</div>
|
|
350
|
+
<canvas id="agentTimelineChart" class="chart-canvas"></canvas>
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
<div class="chart-card">
|
|
354
|
+
<div class="chart-title">
|
|
355
|
+
Workflow Efficiency Score
|
|
356
|
+
</div>
|
|
357
|
+
<canvas id="workflowEfficiencyChart" class="chart-canvas"></canvas>
|
|
358
|
+
</div>
|
|
308
359
|
</div>
|
|
309
|
-
<canvas id="toolChart" class="chart-canvas"></canvas>
|
|
310
360
|
</div>
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
361
|
+
|
|
362
|
+
<!-- SECTION 3: Productivity Analytics -->
|
|
363
|
+
<div class="chart-section">
|
|
364
|
+
<div class="section-header">
|
|
365
|
+
<h3 class="section-title">📈 Productivity Analytics</h3>
|
|
366
|
+
<p class="section-description">Track project activity and tool utilization</p>
|
|
315
367
|
</div>
|
|
316
|
-
<div
|
|
317
|
-
|
|
368
|
+
<div class="section-charts">
|
|
369
|
+
<div class="chart-card">
|
|
370
|
+
<div class="chart-title">
|
|
371
|
+
Project Activity Distribution
|
|
372
|
+
</div>
|
|
373
|
+
<canvas id="projectChart" class="chart-canvas"></canvas>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<div class="chart-card">
|
|
377
|
+
<div class="chart-title">
|
|
378
|
+
Tool Usage Patterns
|
|
379
|
+
</div>
|
|
380
|
+
<canvas id="toolChart" class="chart-canvas"></canvas>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
<div class="chart-card">
|
|
384
|
+
<div class="chart-title">
|
|
385
|
+
Daily Productivity Trends
|
|
386
|
+
</div>
|
|
387
|
+
<canvas id="productivityChart" class="chart-canvas"></canvas>
|
|
388
|
+
</div>
|
|
318
389
|
</div>
|
|
319
390
|
</div>
|
|
320
391
|
</div>
|
|
@@ -825,15 +896,19 @@ class DashboardPage {
|
|
|
825
896
|
*/
|
|
826
897
|
async loadInitialData() {
|
|
827
898
|
try {
|
|
828
|
-
const [conversationsData, statesData] = await Promise.all([
|
|
899
|
+
const [conversationsData, statesData, agentData] = await Promise.all([
|
|
829
900
|
this.dataService.getConversations(),
|
|
830
|
-
this.dataService.getConversationStates()
|
|
901
|
+
this.dataService.getConversationStates(),
|
|
902
|
+
this.dataService.cachedFetch('/api/agents')
|
|
831
903
|
]);
|
|
832
904
|
|
|
833
905
|
this.stateService.updateConversations(conversationsData.conversations);
|
|
834
906
|
this.stateService.updateSummary(conversationsData.summary);
|
|
835
907
|
this.stateService.updateConversationStates(statesData);
|
|
836
908
|
|
|
909
|
+
// Store agent data for charts
|
|
910
|
+
this.agentData = agentData;
|
|
911
|
+
|
|
837
912
|
// Update dashboard with original format
|
|
838
913
|
this.updateSummaryDisplay(
|
|
839
914
|
conversationsData.summary,
|
|
@@ -843,6 +918,7 @@ class DashboardPage {
|
|
|
843
918
|
|
|
844
919
|
this.updateLastUpdateTime();
|
|
845
920
|
this.updateChartData(conversationsData);
|
|
921
|
+
this.updateAgentCharts(agentData);
|
|
846
922
|
} catch (error) {
|
|
847
923
|
console.error('Error loading initial data:', error);
|
|
848
924
|
|
|
@@ -948,6 +1024,11 @@ class DashboardPage {
|
|
|
948
1024
|
this.updateTokenBreakdown(detailedTokenUsage);
|
|
949
1025
|
}
|
|
950
1026
|
|
|
1027
|
+
// Update agent metrics if available
|
|
1028
|
+
if (this.agentData) {
|
|
1029
|
+
this.updateAgentMetrics(this.agentData);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
951
1032
|
// Store data for chart updates
|
|
952
1033
|
this.allData = allData;
|
|
953
1034
|
}
|
|
@@ -985,6 +1066,44 @@ class DashboardPage {
|
|
|
985
1066
|
if (cacheTokens) cacheTokens.textContent = totalCache.toLocaleString();
|
|
986
1067
|
}
|
|
987
1068
|
|
|
1069
|
+
/**
|
|
1070
|
+
* Update agent metrics in the agents card
|
|
1071
|
+
* @param {Object} agentData - Agent analytics data
|
|
1072
|
+
*/
|
|
1073
|
+
updateAgentMetrics(agentData) {
|
|
1074
|
+
if (!agentData) return;
|
|
1075
|
+
|
|
1076
|
+
const totalAgentInvocations = this.container.querySelector('#totalAgentInvocations');
|
|
1077
|
+
const totalAgentTypes = this.container.querySelector('#totalAgentTypes');
|
|
1078
|
+
const topAgentName = this.container.querySelector('#topAgentName');
|
|
1079
|
+
const agentAdoption = this.container.querySelector('#agentAdoption');
|
|
1080
|
+
|
|
1081
|
+
// Update primary metric - total invocations
|
|
1082
|
+
if (totalAgentInvocations) {
|
|
1083
|
+
totalAgentInvocations.textContent = agentData.totalAgentInvocations?.toLocaleString() || '0';
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// Update secondary metrics
|
|
1087
|
+
if (totalAgentTypes) {
|
|
1088
|
+
totalAgentTypes.textContent = agentData.totalAgentTypes?.toLocaleString() || '0';
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
if (topAgentName) {
|
|
1092
|
+
const topAgent = agentData.agentStats?.[0];
|
|
1093
|
+
if (topAgent) {
|
|
1094
|
+
topAgentName.textContent = topAgent.name;
|
|
1095
|
+
topAgentName.title = `${topAgent.totalInvocations} uses`;
|
|
1096
|
+
} else {
|
|
1097
|
+
topAgentName.textContent = 'None';
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (agentAdoption) {
|
|
1102
|
+
const adoptionRate = agentData.efficiency?.adoptionRate || '0';
|
|
1103
|
+
agentAdoption.textContent = adoptionRate + '%';
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
|
|
988
1107
|
/**
|
|
989
1108
|
* Show token popover
|
|
990
1109
|
*/
|
|
@@ -1050,6 +1169,9 @@ class DashboardPage {
|
|
|
1050
1169
|
if (this.allData) {
|
|
1051
1170
|
this.updateChartData(this.allData);
|
|
1052
1171
|
}
|
|
1172
|
+
if (this.agentData) {
|
|
1173
|
+
this.updateAgentCharts(this.agentData);
|
|
1174
|
+
}
|
|
1053
1175
|
}
|
|
1054
1176
|
|
|
1055
1177
|
/**
|
|
@@ -1095,9 +1217,17 @@ class DashboardPage {
|
|
|
1095
1217
|
updateChartData(data) {
|
|
1096
1218
|
if (!data || !data.conversations) return;
|
|
1097
1219
|
|
|
1220
|
+
// Token Analytics Section
|
|
1098
1221
|
this.updateTokenChart(data.conversations);
|
|
1222
|
+
this.updateTokenTypeChart(data);
|
|
1223
|
+
this.updateTokenTimelineChart(data);
|
|
1224
|
+
|
|
1225
|
+
// Productivity Analytics Section
|
|
1099
1226
|
this.updateProjectChart(data.conversations);
|
|
1100
1227
|
this.updateToolChart(data.conversations);
|
|
1228
|
+
this.updateProductivityChart(data);
|
|
1229
|
+
|
|
1230
|
+
// Legacy tool summary (keeping for now)
|
|
1101
1231
|
this.updateToolSummary(data.conversations);
|
|
1102
1232
|
}
|
|
1103
1233
|
|
|
@@ -1256,6 +1386,647 @@ class DashboardPage {
|
|
|
1256
1386
|
`;
|
|
1257
1387
|
}
|
|
1258
1388
|
|
|
1389
|
+
/**
|
|
1390
|
+
* Update agent usage charts
|
|
1391
|
+
* @param {Object} agentData - Agent analytics data
|
|
1392
|
+
*/
|
|
1393
|
+
updateAgentCharts(agentData) {
|
|
1394
|
+
if (!agentData || !agentData.agentStats) {
|
|
1395
|
+
console.warn('No agent data available for charts');
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
this.updateAgentUsageChart(agentData);
|
|
1400
|
+
this.updateAgentTimelineChart(agentData);
|
|
1401
|
+
this.updateWorkflowEfficiencyChart(agentData);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* Update agent usage distribution chart
|
|
1406
|
+
* @param {Object} agentData - Agent analytics data
|
|
1407
|
+
*/
|
|
1408
|
+
updateAgentUsageChart(agentData) {
|
|
1409
|
+
const canvas = this.container.querySelector('#agentUsageChart');
|
|
1410
|
+
if (!canvas) {
|
|
1411
|
+
console.warn('Agent usage chart canvas not found');
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// Destroy existing chart if it exists
|
|
1416
|
+
const existingChart = Chart.getChart(canvas);
|
|
1417
|
+
if (existingChart) {
|
|
1418
|
+
existingChart.destroy();
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
const ctx = canvas.getContext('2d');
|
|
1422
|
+
const agentStats = agentData.agentStats || [];
|
|
1423
|
+
|
|
1424
|
+
if (agentStats.length === 0) {
|
|
1425
|
+
// Show "no data" message
|
|
1426
|
+
ctx.fillStyle = '#7d8590';
|
|
1427
|
+
ctx.textAlign = 'center';
|
|
1428
|
+
ctx.font = '14px Monaco, monospace';
|
|
1429
|
+
ctx.fillText('No agent usage data', canvas.width / 2, canvas.height / 2);
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
new Chart(ctx, {
|
|
1434
|
+
type: 'doughnut',
|
|
1435
|
+
data: {
|
|
1436
|
+
labels: agentStats.map(agent => agent.name),
|
|
1437
|
+
datasets: [{
|
|
1438
|
+
data: agentStats.map(agent => agent.totalInvocations),
|
|
1439
|
+
backgroundColor: agentStats.map(agent => agent.color),
|
|
1440
|
+
borderColor: '#0d1117',
|
|
1441
|
+
borderWidth: 2,
|
|
1442
|
+
hoverBorderWidth: 3
|
|
1443
|
+
}]
|
|
1444
|
+
},
|
|
1445
|
+
options: {
|
|
1446
|
+
responsive: true,
|
|
1447
|
+
maintainAspectRatio: false,
|
|
1448
|
+
plugins: {
|
|
1449
|
+
legend: {
|
|
1450
|
+
position: 'bottom',
|
|
1451
|
+
labels: {
|
|
1452
|
+
color: '#c9d1d9',
|
|
1453
|
+
padding: 10,
|
|
1454
|
+
usePointStyle: true,
|
|
1455
|
+
font: {
|
|
1456
|
+
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace",
|
|
1457
|
+
size: 11
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
},
|
|
1461
|
+
tooltip: {
|
|
1462
|
+
titleFont: {
|
|
1463
|
+
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
|
|
1464
|
+
},
|
|
1465
|
+
bodyFont: {
|
|
1466
|
+
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
|
|
1467
|
+
},
|
|
1468
|
+
callbacks: {
|
|
1469
|
+
label: function(context) {
|
|
1470
|
+
const agent = agentStats[context.dataIndex];
|
|
1471
|
+
return `${agent.name}: ${context.parsed} uses (${agent.uniqueConversations} conversations)`;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
},
|
|
1476
|
+
cutout: '60%'
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* Update agent usage timeline chart
|
|
1483
|
+
* @param {Object} agentData - Agent analytics data
|
|
1484
|
+
*/
|
|
1485
|
+
updateAgentTimelineChart(agentData) {
|
|
1486
|
+
const canvas = this.container.querySelector('#agentTimelineChart');
|
|
1487
|
+
if (!canvas) {
|
|
1488
|
+
console.warn('Agent timeline chart canvas not found');
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// Destroy existing chart if it exists
|
|
1493
|
+
const existingChart = Chart.getChart(canvas);
|
|
1494
|
+
if (existingChart) {
|
|
1495
|
+
existingChart.destroy();
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
const ctx = canvas.getContext('2d');
|
|
1499
|
+
const usageByDay = agentData.usageByDay || [];
|
|
1500
|
+
|
|
1501
|
+
if (usageByDay.length === 0) {
|
|
1502
|
+
// Show "no data" message
|
|
1503
|
+
ctx.fillStyle = '#7d8590';
|
|
1504
|
+
ctx.textAlign = 'center';
|
|
1505
|
+
ctx.font = '14px Monaco, monospace';
|
|
1506
|
+
ctx.fillText('No timeline data', canvas.width / 2, canvas.height / 2);
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
new Chart(ctx, {
|
|
1511
|
+
type: 'line',
|
|
1512
|
+
data: {
|
|
1513
|
+
labels: usageByDay.map(d => new Date(d.date).toLocaleDateString()),
|
|
1514
|
+
datasets: [{
|
|
1515
|
+
label: 'Agent Usage',
|
|
1516
|
+
data: usageByDay.map(d => d.count),
|
|
1517
|
+
borderColor: '#3fb950',
|
|
1518
|
+
backgroundColor: 'rgba(63, 185, 80, 0.1)',
|
|
1519
|
+
borderWidth: 2,
|
|
1520
|
+
fill: true,
|
|
1521
|
+
tension: 0.3,
|
|
1522
|
+
pointBackgroundColor: '#3fb950',
|
|
1523
|
+
pointBorderColor: '#ffffff',
|
|
1524
|
+
pointBorderWidth: 2,
|
|
1525
|
+
pointRadius: 4,
|
|
1526
|
+
pointHoverRadius: 6
|
|
1527
|
+
}]
|
|
1528
|
+
},
|
|
1529
|
+
options: {
|
|
1530
|
+
responsive: true,
|
|
1531
|
+
maintainAspectRatio: false,
|
|
1532
|
+
plugins: {
|
|
1533
|
+
legend: {
|
|
1534
|
+
labels: {
|
|
1535
|
+
color: '#c9d1d9',
|
|
1536
|
+
font: {
|
|
1537
|
+
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace",
|
|
1538
|
+
size: 11
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
},
|
|
1542
|
+
tooltip: {
|
|
1543
|
+
titleFont: {
|
|
1544
|
+
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
|
|
1545
|
+
},
|
|
1546
|
+
bodyFont: {
|
|
1547
|
+
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
|
|
1548
|
+
},
|
|
1549
|
+
callbacks: {
|
|
1550
|
+
label: function(context) {
|
|
1551
|
+
return `Agent invocations: ${context.parsed.y}`;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
},
|
|
1556
|
+
scales: {
|
|
1557
|
+
x: {
|
|
1558
|
+
ticks: {
|
|
1559
|
+
color: '#7d8590',
|
|
1560
|
+
font: {
|
|
1561
|
+
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
|
|
1562
|
+
}
|
|
1563
|
+
},
|
|
1564
|
+
grid: {
|
|
1565
|
+
color: '#30363d'
|
|
1566
|
+
}
|
|
1567
|
+
},
|
|
1568
|
+
y: {
|
|
1569
|
+
beginAtZero: true,
|
|
1570
|
+
ticks: {
|
|
1571
|
+
color: '#7d8590',
|
|
1572
|
+
font: {
|
|
1573
|
+
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
|
|
1574
|
+
},
|
|
1575
|
+
stepSize: 1
|
|
1576
|
+
},
|
|
1577
|
+
grid: {
|
|
1578
|
+
color: '#30363d'
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
/**
|
|
1587
|
+
* Update workflow efficiency chart
|
|
1588
|
+
* @param {Object} agentData - Agent analytics data
|
|
1589
|
+
*/
|
|
1590
|
+
updateWorkflowEfficiencyChart(agentData) {
|
|
1591
|
+
const canvas = this.container.querySelector('#workflowEfficiencyChart');
|
|
1592
|
+
if (!canvas) {
|
|
1593
|
+
console.warn('Workflow efficiency chart canvas not found');
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// Destroy existing chart if it exists
|
|
1598
|
+
const existingChart = Chart.getChart(canvas);
|
|
1599
|
+
if (existingChart) {
|
|
1600
|
+
existingChart.destroy();
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
const ctx = canvas.getContext('2d');
|
|
1604
|
+
const efficiency = agentData.efficiency || {};
|
|
1605
|
+
|
|
1606
|
+
const data = {
|
|
1607
|
+
labels: ['Adoption Rate', 'Workflow Completion', 'Time Efficiency', 'Success Rate'],
|
|
1608
|
+
datasets: [{
|
|
1609
|
+
label: 'Efficiency %',
|
|
1610
|
+
data: [
|
|
1611
|
+
efficiency.adoptionRate || 0,
|
|
1612
|
+
efficiency.workflowCompletion || 0,
|
|
1613
|
+
efficiency.timeEfficiency || 0,
|
|
1614
|
+
efficiency.successRate || 0
|
|
1615
|
+
],
|
|
1616
|
+
backgroundColor: [
|
|
1617
|
+
'rgba(63, 185, 80, 0.8)',
|
|
1618
|
+
'rgba(88, 166, 255, 0.8)',
|
|
1619
|
+
'rgba(249, 115, 22, 0.8)',
|
|
1620
|
+
'rgba(213, 116, 85, 0.8)'
|
|
1621
|
+
],
|
|
1622
|
+
borderColor: [
|
|
1623
|
+
'#3fb950',
|
|
1624
|
+
'#58a6ff',
|
|
1625
|
+
'#f97316',
|
|
1626
|
+
'#d57455'
|
|
1627
|
+
],
|
|
1628
|
+
borderWidth: 2
|
|
1629
|
+
}]
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
new Chart(ctx, {
|
|
1633
|
+
type: 'radar',
|
|
1634
|
+
data: data,
|
|
1635
|
+
options: {
|
|
1636
|
+
responsive: true,
|
|
1637
|
+
maintainAspectRatio: false,
|
|
1638
|
+
plugins: {
|
|
1639
|
+
legend: {
|
|
1640
|
+
display: false
|
|
1641
|
+
},
|
|
1642
|
+
tooltip: {
|
|
1643
|
+
callbacks: {
|
|
1644
|
+
label: function(context) {
|
|
1645
|
+
const label = context.label;
|
|
1646
|
+
const value = context.parsed.r;
|
|
1647
|
+
return `${label}: ${value.toFixed(1)}%`;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
},
|
|
1652
|
+
scales: {
|
|
1653
|
+
r: {
|
|
1654
|
+
beginAtZero: true,
|
|
1655
|
+
max: 100,
|
|
1656
|
+
ticks: {
|
|
1657
|
+
stepSize: 20,
|
|
1658
|
+
color: '#7d8590',
|
|
1659
|
+
backdropColor: 'transparent'
|
|
1660
|
+
},
|
|
1661
|
+
grid: {
|
|
1662
|
+
color: '#30363d'
|
|
1663
|
+
},
|
|
1664
|
+
angleLines: {
|
|
1665
|
+
color: '#30363d'
|
|
1666
|
+
},
|
|
1667
|
+
pointLabels: {
|
|
1668
|
+
color: '#c9d1d9',
|
|
1669
|
+
font: {
|
|
1670
|
+
size: 11
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/**
|
|
1680
|
+
* Update token type distribution chart
|
|
1681
|
+
* @param {Object} data - Chart data
|
|
1682
|
+
*/
|
|
1683
|
+
updateTokenTypeChart(data) {
|
|
1684
|
+
const canvas = this.container.querySelector('#tokenTypeChart');
|
|
1685
|
+
if (!canvas) {
|
|
1686
|
+
console.warn('Token type chart canvas not found');
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
const existingChart = Chart.getChart(canvas);
|
|
1691
|
+
if (existingChart) existingChart.destroy();
|
|
1692
|
+
|
|
1693
|
+
const ctx = canvas.getContext('2d');
|
|
1694
|
+
const tokenData = data.detailedTokenUsage || {};
|
|
1695
|
+
|
|
1696
|
+
console.log('Token type chart data:', tokenData);
|
|
1697
|
+
|
|
1698
|
+
const chartData = [
|
|
1699
|
+
tokenData.inputTokens || 0,
|
|
1700
|
+
tokenData.outputTokens || 0,
|
|
1701
|
+
tokenData.cacheCreationTokens || 0,
|
|
1702
|
+
tokenData.cacheReadTokens || 0
|
|
1703
|
+
];
|
|
1704
|
+
|
|
1705
|
+
const totalTokens = chartData.reduce((sum, val) => sum + val, 0);
|
|
1706
|
+
|
|
1707
|
+
if (totalTokens === 0) {
|
|
1708
|
+
// Show "no data" message
|
|
1709
|
+
ctx.fillStyle = '#7d8590';
|
|
1710
|
+
ctx.textAlign = 'center';
|
|
1711
|
+
ctx.font = '14px Monaco, monospace';
|
|
1712
|
+
ctx.fillText('No token data available', canvas.width / 2, canvas.height / 2);
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
new Chart(ctx, {
|
|
1717
|
+
type: 'doughnut',
|
|
1718
|
+
data: {
|
|
1719
|
+
labels: ['Input Tokens', 'Output Tokens', 'Cache Creation', 'Cache Read'],
|
|
1720
|
+
datasets: [{
|
|
1721
|
+
data: chartData,
|
|
1722
|
+
backgroundColor: ['#3fb950', '#58a6ff', '#f97316', '#d57455'],
|
|
1723
|
+
borderColor: '#0d1117',
|
|
1724
|
+
borderWidth: 2
|
|
1725
|
+
}]
|
|
1726
|
+
},
|
|
1727
|
+
options: {
|
|
1728
|
+
responsive: true,
|
|
1729
|
+
maintainAspectRatio: false,
|
|
1730
|
+
plugins: {
|
|
1731
|
+
legend: {
|
|
1732
|
+
position: 'bottom',
|
|
1733
|
+
labels: {
|
|
1734
|
+
color: '#c9d1d9',
|
|
1735
|
+
font: { size: 11 }
|
|
1736
|
+
}
|
|
1737
|
+
},
|
|
1738
|
+
tooltip: {
|
|
1739
|
+
callbacks: {
|
|
1740
|
+
label: function(context) {
|
|
1741
|
+
const label = context.label;
|
|
1742
|
+
const value = context.parsed;
|
|
1743
|
+
const total = context.dataset.data.reduce((sum, val) => sum + val, 0);
|
|
1744
|
+
const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : 0;
|
|
1745
|
+
return `${label}: ${value.toLocaleString()} tokens (${percentage}%)`;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
/**
|
|
1755
|
+
* Update token usage over time chart
|
|
1756
|
+
* @param {Object} data - Chart data
|
|
1757
|
+
*/
|
|
1758
|
+
updateTokenTimelineChart(data) {
|
|
1759
|
+
const canvas = this.container.querySelector('#tokenTimelineChart');
|
|
1760
|
+
if (!canvas) {
|
|
1761
|
+
console.warn('Token timeline chart canvas not found');
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
const existingChart = Chart.getChart(canvas);
|
|
1766
|
+
if (existingChart) existingChart.destroy();
|
|
1767
|
+
|
|
1768
|
+
const ctx = canvas.getContext('2d');
|
|
1769
|
+
const conversations = data.conversations || [];
|
|
1770
|
+
|
|
1771
|
+
if (conversations.length === 0) {
|
|
1772
|
+
// Show "no data" message
|
|
1773
|
+
ctx.fillStyle = '#7d8590';
|
|
1774
|
+
ctx.textAlign = 'center';
|
|
1775
|
+
ctx.font = '14px Monaco, monospace';
|
|
1776
|
+
ctx.fillText('No token timeline data', canvas.width / 2, canvas.height / 2);
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
// Calculate daily token usage
|
|
1781
|
+
const dailyTokens = this.calculateDailyTokenUsage(conversations);
|
|
1782
|
+
|
|
1783
|
+
new Chart(ctx, {
|
|
1784
|
+
type: 'line',
|
|
1785
|
+
data: {
|
|
1786
|
+
labels: dailyTokens.labels,
|
|
1787
|
+
datasets: [{
|
|
1788
|
+
label: 'Input Tokens',
|
|
1789
|
+
data: dailyTokens.inputTokens,
|
|
1790
|
+
borderColor: '#3fb950',
|
|
1791
|
+
backgroundColor: 'rgba(63, 185, 80, 0.1)',
|
|
1792
|
+
fill: false,
|
|
1793
|
+
tension: 0.3,
|
|
1794
|
+
pointBackgroundColor: '#3fb950',
|
|
1795
|
+
pointBorderColor: '#ffffff',
|
|
1796
|
+
pointBorderWidth: 2,
|
|
1797
|
+
pointRadius: 3,
|
|
1798
|
+
pointHoverRadius: 5
|
|
1799
|
+
}, {
|
|
1800
|
+
label: 'Output Tokens',
|
|
1801
|
+
data: dailyTokens.outputTokens,
|
|
1802
|
+
borderColor: '#58a6ff',
|
|
1803
|
+
backgroundColor: 'rgba(88, 166, 255, 0.1)',
|
|
1804
|
+
fill: false,
|
|
1805
|
+
tension: 0.3,
|
|
1806
|
+
pointBackgroundColor: '#58a6ff',
|
|
1807
|
+
pointBorderColor: '#ffffff',
|
|
1808
|
+
pointBorderWidth: 2,
|
|
1809
|
+
pointRadius: 3,
|
|
1810
|
+
pointHoverRadius: 5
|
|
1811
|
+
}, {
|
|
1812
|
+
label: 'Cache Usage',
|
|
1813
|
+
data: dailyTokens.cacheTokens,
|
|
1814
|
+
borderColor: '#f97316',
|
|
1815
|
+
backgroundColor: 'rgba(249, 115, 22, 0.1)',
|
|
1816
|
+
fill: false,
|
|
1817
|
+
tension: 0.3,
|
|
1818
|
+
pointBackgroundColor: '#f97316',
|
|
1819
|
+
pointBorderColor: '#ffffff',
|
|
1820
|
+
pointBorderWidth: 2,
|
|
1821
|
+
pointRadius: 3,
|
|
1822
|
+
pointHoverRadius: 5
|
|
1823
|
+
}]
|
|
1824
|
+
},
|
|
1825
|
+
options: {
|
|
1826
|
+
responsive: true,
|
|
1827
|
+
maintainAspectRatio: false,
|
|
1828
|
+
plugins: {
|
|
1829
|
+
legend: {
|
|
1830
|
+
position: 'bottom',
|
|
1831
|
+
labels: {
|
|
1832
|
+
color: '#c9d1d9',
|
|
1833
|
+
font: { size: 11 },
|
|
1834
|
+
padding: 15,
|
|
1835
|
+
usePointStyle: true
|
|
1836
|
+
}
|
|
1837
|
+
},
|
|
1838
|
+
tooltip: {
|
|
1839
|
+
callbacks: {
|
|
1840
|
+
label: function(context) {
|
|
1841
|
+
const label = context.dataset.label;
|
|
1842
|
+
const value = context.parsed.y;
|
|
1843
|
+
return `${label}: ${value.toLocaleString()} tokens`;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
},
|
|
1848
|
+
scales: {
|
|
1849
|
+
x: {
|
|
1850
|
+
ticks: { color: '#7d8590' },
|
|
1851
|
+
grid: { color: '#30363d' }
|
|
1852
|
+
},
|
|
1853
|
+
y: {
|
|
1854
|
+
beginAtZero: true,
|
|
1855
|
+
ticks: {
|
|
1856
|
+
color: '#7d8590',
|
|
1857
|
+
callback: function(value) {
|
|
1858
|
+
return value.toLocaleString();
|
|
1859
|
+
}
|
|
1860
|
+
},
|
|
1861
|
+
grid: { color: '#30363d' }
|
|
1862
|
+
}
|
|
1863
|
+
},
|
|
1864
|
+
interaction: {
|
|
1865
|
+
intersect: false,
|
|
1866
|
+
mode: 'index'
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
/**
|
|
1873
|
+
* Update daily productivity trends chart
|
|
1874
|
+
* @param {Object} data - Chart data
|
|
1875
|
+
*/
|
|
1876
|
+
updateProductivityChart(data) {
|
|
1877
|
+
const canvas = this.container.querySelector('#productivityChart');
|
|
1878
|
+
if (!canvas) return;
|
|
1879
|
+
|
|
1880
|
+
const existingChart = Chart.getChart(canvas);
|
|
1881
|
+
if (existingChart) existingChart.destroy();
|
|
1882
|
+
|
|
1883
|
+
const ctx = canvas.getContext('2d');
|
|
1884
|
+
|
|
1885
|
+
// Calculate productivity metrics by day
|
|
1886
|
+
const dailyData = this.calculateDailyProductivity(data);
|
|
1887
|
+
|
|
1888
|
+
new Chart(ctx, {
|
|
1889
|
+
type: 'line',
|
|
1890
|
+
data: {
|
|
1891
|
+
labels: dailyData.labels,
|
|
1892
|
+
datasets: [{
|
|
1893
|
+
label: 'Messages per Day',
|
|
1894
|
+
data: dailyData.messages,
|
|
1895
|
+
borderColor: '#3fb950',
|
|
1896
|
+
backgroundColor: 'rgba(63, 185, 80, 0.1)',
|
|
1897
|
+
fill: true,
|
|
1898
|
+
tension: 0.3
|
|
1899
|
+
}, {
|
|
1900
|
+
label: 'Tokens per Day',
|
|
1901
|
+
data: dailyData.tokens,
|
|
1902
|
+
borderColor: '#58a6ff',
|
|
1903
|
+
backgroundColor: 'rgba(88, 166, 255, 0.1)',
|
|
1904
|
+
fill: true,
|
|
1905
|
+
tension: 0.3,
|
|
1906
|
+
yAxisID: 'y1'
|
|
1907
|
+
}]
|
|
1908
|
+
},
|
|
1909
|
+
options: {
|
|
1910
|
+
responsive: true,
|
|
1911
|
+
maintainAspectRatio: false,
|
|
1912
|
+
plugins: {
|
|
1913
|
+
legend: {
|
|
1914
|
+
labels: { color: '#c9d1d9', font: { size: 11 } }
|
|
1915
|
+
},
|
|
1916
|
+
tooltip: {
|
|
1917
|
+
callbacks: {
|
|
1918
|
+
label: function(context) {
|
|
1919
|
+
const label = context.dataset.label;
|
|
1920
|
+
const value = context.parsed.y;
|
|
1921
|
+
if (label === 'Messages per Day') {
|
|
1922
|
+
return `${label}: ${value} messages`;
|
|
1923
|
+
} else {
|
|
1924
|
+
return `${label}: ${value.toLocaleString()} tokens`;
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
},
|
|
1930
|
+
scales: {
|
|
1931
|
+
x: {
|
|
1932
|
+
ticks: { color: '#7d8590' },
|
|
1933
|
+
grid: { color: '#30363d' }
|
|
1934
|
+
},
|
|
1935
|
+
y: {
|
|
1936
|
+
type: 'linear',
|
|
1937
|
+
display: true,
|
|
1938
|
+
position: 'left',
|
|
1939
|
+
ticks: { color: '#7d8590' },
|
|
1940
|
+
grid: { color: '#30363d' }
|
|
1941
|
+
},
|
|
1942
|
+
y1: {
|
|
1943
|
+
type: 'linear',
|
|
1944
|
+
display: true,
|
|
1945
|
+
position: 'right',
|
|
1946
|
+
ticks: { color: '#7d8590' },
|
|
1947
|
+
grid: { drawOnChartArea: false }
|
|
1948
|
+
}
|
|
1949
|
+
},
|
|
1950
|
+
interaction: {
|
|
1951
|
+
intersect: false,
|
|
1952
|
+
mode: 'index'
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* Calculate daily productivity metrics
|
|
1960
|
+
* @param {Object} data - Raw data
|
|
1961
|
+
* @returns {Object} Processed daily data
|
|
1962
|
+
*/
|
|
1963
|
+
/**
|
|
1964
|
+
* Calculate daily token usage from conversations
|
|
1965
|
+
* @param {Array} conversations - Array of conversation objects
|
|
1966
|
+
* @returns {Object} Daily token data
|
|
1967
|
+
*/
|
|
1968
|
+
calculateDailyTokenUsage(conversations) {
|
|
1969
|
+
const dailyData = {};
|
|
1970
|
+
const { fromDate, toDate } = this.getDateRange();
|
|
1971
|
+
|
|
1972
|
+
conversations.forEach(conv => {
|
|
1973
|
+
const convDate = new Date(conv.lastModified);
|
|
1974
|
+
if (convDate >= fromDate && convDate <= toDate) {
|
|
1975
|
+
const dateKey = convDate.toISOString().split('T')[0]; // YYYY-MM-DD
|
|
1976
|
+
|
|
1977
|
+
if (!dailyData[dateKey]) {
|
|
1978
|
+
dailyData[dateKey] = {
|
|
1979
|
+
inputTokens: 0,
|
|
1980
|
+
outputTokens: 0,
|
|
1981
|
+
cacheTokens: 0
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
if (conv.tokenUsage) {
|
|
1986
|
+
dailyData[dateKey].inputTokens += conv.tokenUsage.inputTokens || 0;
|
|
1987
|
+
dailyData[dateKey].outputTokens += conv.tokenUsage.outputTokens || 0;
|
|
1988
|
+
dailyData[dateKey].cacheTokens += (conv.tokenUsage.cacheCreationTokens || 0) + (conv.tokenUsage.cacheReadTokens || 0);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
|
|
1993
|
+
// Sort dates and create arrays
|
|
1994
|
+
const sortedDates = Object.keys(dailyData).sort();
|
|
1995
|
+
const labels = sortedDates.map(date => new Date(date).toLocaleDateString());
|
|
1996
|
+
const inputTokens = sortedDates.map(date => dailyData[date].inputTokens);
|
|
1997
|
+
const outputTokens = sortedDates.map(date => dailyData[date].outputTokens);
|
|
1998
|
+
const cacheTokens = sortedDates.map(date => dailyData[date].cacheTokens);
|
|
1999
|
+
|
|
2000
|
+
return { labels, inputTokens, outputTokens, cacheTokens };
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
calculateDailyProductivity(data) {
|
|
2004
|
+
const conversations = data.conversations || [];
|
|
2005
|
+
const dailyStats = {};
|
|
2006
|
+
|
|
2007
|
+
// Group data by day
|
|
2008
|
+
conversations.forEach(conv => {
|
|
2009
|
+
if (!conv.lastModified) return;
|
|
2010
|
+
|
|
2011
|
+
const date = new Date(conv.lastModified).toDateString();
|
|
2012
|
+
if (!dailyStats[date]) {
|
|
2013
|
+
dailyStats[date] = { messages: 0, tokens: 0 };
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
dailyStats[date].messages += conv.messageCount || 0;
|
|
2017
|
+
dailyStats[date].tokens += (conv.tokenUsage?.inputTokens || 0) + (conv.tokenUsage?.outputTokens || 0);
|
|
2018
|
+
});
|
|
2019
|
+
|
|
2020
|
+
// Convert to arrays for chart
|
|
2021
|
+
const sortedDates = Object.keys(dailyStats).sort((a, b) => new Date(a) - new Date(b));
|
|
2022
|
+
|
|
2023
|
+
return {
|
|
2024
|
+
labels: sortedDates.map(date => new Date(date).toLocaleDateString()),
|
|
2025
|
+
messages: sortedDates.map(date => dailyStats[date].messages),
|
|
2026
|
+
tokens: sortedDates.map(date => Math.round(dailyStats[date].tokens / 1000)) // Convert to K tokens
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
|
|
1259
2030
|
/**
|
|
1260
2031
|
* Update usage chart
|
|
1261
2032
|
* @param {string} period - Time period
|