122 lines
6.3 KiB
PowerShell
122 lines
6.3 KiB
PowerShell
#!/usr/bin/env pwsh
|
|
# CASAN H6 AgentOps Dashboard Generator (auto-refresh HTML)
|
|
# Usage:
|
|
# generate-agentops-dashboard.ps1 [-FeatureId <id>] [-OutputHtml <path>]
|
|
|
|
param(
|
|
[string]$FeatureId = "latest",
|
|
[string]$OutputHtml = ""
|
|
)
|
|
|
|
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
|
|
$projectRoot = (Resolve-Path (Join-Path $scriptDir "../../..")).Path
|
|
$metricsLog = if ($env:CASAN_TELEMETRY_METRICS_LOG) { $env:CASAN_TELEMETRY_METRICS_LOG } elseif ($env:CASAN_DASHBOARD_METRICS) { $env:CASAN_DASHBOARD_METRICS } else { Join-Path $projectRoot ".specify/logs/cost/metrics.jsonl" }
|
|
$alertLog = if ($env:CASAN_TELEMETRY_ALERTS_LOG) { $env:CASAN_TELEMETRY_ALERTS_LOG } elseif ($env:CASAN_DASHBOARD_ALERTS) { $env:CASAN_DASHBOARD_ALERTS } else { Join-Path $projectRoot ".specify/agentops/alerts.log" }
|
|
|
|
if (!$OutputHtml) { $OutputHtml = Join-Path $projectRoot "docs/output/casan/agentops-dashboard.html" }
|
|
if (!(Test-Path (Split-Path $OutputHtml -Parent))) { New-Item -ItemType Directory -Force -Path (Split-Path $OutputHtml -Parent) | Out-Null }
|
|
|
|
$generatedAt = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
|
|
# ── Read metrics ───────────────────────────────────────────────────────────
|
|
$metrics = @()
|
|
if (Test-Path $metricsLog) {
|
|
$metrics = Get-Content $metricsLog | ForEach-Object {
|
|
try { $_ | ConvertFrom-Json } catch { $null }
|
|
} | Where-Object { $_ }
|
|
}
|
|
|
|
$alerts = @()
|
|
if (Test-Path $alertLog) {
|
|
$alerts = Get-Content $alertLog | ForEach-Object {
|
|
try { $_ | ConvertFrom-Json } catch { $null }
|
|
} | Where-Object { $_ }
|
|
}
|
|
|
|
# ── Aggregations ───────────────────────────────────────────────────────────
|
|
$totalRuns = $metrics.Count
|
|
$totalCost = [math]::Round(($metrics | Measure-Object -Property cost_estimate -Sum).Sum, 4)
|
|
$totalTokens = ($metrics | Measure-Object -Property tokens_estimated -Sum).Sum
|
|
$avgLatency = if ($totalRuns) { [math]::Round(($metrics | Measure-Object -Property latency_ms -Average).Average, 0) } else { 0 }
|
|
$failedRuns = ($metrics | Where-Object { $_.status -eq "failed" }).Count
|
|
$passRate = if ($totalRuns) { [math]::Round(($totalRuns - $failedRuns) / $totalRuns * 100, 1) } else { 100 }
|
|
$totalAlerts = $alerts.Count
|
|
|
|
# Per-step table rows
|
|
$stepRows = $metrics | Group-Object step | ForEach-Object {
|
|
$g = $_
|
|
$avgLat = [math]::Round(($g.Group | Measure-Object latency_ms -Average).Average, 0)
|
|
$totCost = [math]::Round(($g.Group | Measure-Object cost_estimate -Sum).Sum, 4)
|
|
$runs = $g.Group.Count
|
|
$fails = ($g.Group | Where-Object { $_.status -eq "failed" }).Count
|
|
"<tr><td>$($g.Name)</td><td>$runs</td><td>${avgLat}ms</td><td>$$totCost</td><td>$fails</td></tr>"
|
|
}
|
|
|
|
# Alert rows
|
|
$alertRows = $alerts | Select-Object -Last 20 | ForEach-Object {
|
|
$sev = if ($_.severity -eq "WARN") { "style='color:orange'" } else { "style='color:red'" }
|
|
$msg = $_.body.message
|
|
"<tr><td $sev>$($_.severity)</td><td>$($_.timestamp)</td><td>$msg</td></tr>"
|
|
}
|
|
|
|
$html = @"
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>CASAN AgentOps Dashboard — $FeatureId</title>
|
|
<style>
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background:#f5f7fa; margin:0; padding:20px; color:#333 }
|
|
h1 { color:#1a56db; margin-bottom:4px }
|
|
.meta { color:#6b7280; font-size:0.85em; margin-bottom:24px }
|
|
.cards { display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:16px; margin-bottom:28px }
|
|
.card { background:#fff; border-radius:12px; padding:20px; box-shadow:0 1px 4px rgba(0,0,0,.08); text-align:center }
|
|
.card .val { font-size:2em; font-weight:700; color:#1a56db }
|
|
.card .lbl { font-size:0.8em; color:#6b7280; margin-top:4px }
|
|
.card.warn .val { color:#d97706 }
|
|
.card.fail .val { color:#dc2626 }
|
|
table { width:100%; border-collapse:collapse; background:#fff; border-radius:12px; overflow:hidden; box-shadow:0 1px 4px rgba(0,0,0,.08); margin-bottom:24px }
|
|
th { background:#1a56db; color:#fff; padding:10px 14px; text-align:left; font-size:0.85em }
|
|
td { padding:9px 14px; border-bottom:1px solid #f0f0f0; font-size:0.9em }
|
|
tr:last-child td { border-bottom:none }
|
|
h2 { color:#374151; margin:24px 0 8px }
|
|
.badge { display:inline-block; padding:2px 8px; border-radius:99px; font-size:0.75em; font-weight:600 }
|
|
.badge.pass { background:#d1fae5; color:#065f46 }
|
|
.badge.fail { background:#fee2e2; color:#991b1b }
|
|
footer { color:#9ca3af; font-size:0.78em; margin-top:32px }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>CASAN AgentOps Dashboard</h1>
|
|
<div class="meta">Feature: <strong>$FeatureId</strong> | Generated: $generatedAt</div>
|
|
|
|
<div class="cards">
|
|
<div class="card"><div class="val">$totalRuns</div><div class="lbl">Total Runs</div></div>
|
|
<div class="card $(if($passRate -lt 80){'fail'} elseif($passRate -lt 95){'warn'} else {''})"><div class="val">${passRate}%</div><div class="lbl">Pass Rate</div></div>
|
|
<div class="card"><div class="val">${avgLatency}ms</div><div class="lbl">Avg Latency</div></div>
|
|
<div class="card"><div class="val">$totalTokens</div><div class="lbl">Total Tokens (est.)</div></div>
|
|
<div class="card $(if($totalCost -gt 1){'warn'} else {''})"><div class="val">\$$totalCost</div><div class="lbl">Est. Cost (USD)</div></div>
|
|
<div class="card $(if($totalAlerts -gt 0){'warn'} else {''})"><div class="val">$totalAlerts</div><div class="lbl">Alerts</div></div>
|
|
</div>
|
|
|
|
<h2>Per-Step Breakdown</h2>
|
|
<table>
|
|
<thead><tr><th>Step</th><th>Runs</th><th>Avg Latency</th><th>Est. Cost</th><th>Failures</th></tr></thead>
|
|
<tbody>$($stepRows -join "`n")</tbody>
|
|
</table>
|
|
|
|
<h2>Recent Alerts (last 20)</h2>
|
|
<table>
|
|
<thead><tr><th>Severity</th><th>Timestamp</th><th>Message</th></tr></thead>
|
|
<tbody>$(if($alertRows){$alertRows -join "`n"} else {"<tr><td colspan='3' style='color:#10b981;text-align:center'>No alerts — all clear ✓</td></tr>"})</tbody>
|
|
</table>
|
|
|
|
<footer>CASAN Level 4 AgentOps Harness | Auto-generated — do not edit manually | $generatedAt</footer>
|
|
</body>
|
|
</html>
|
|
"@
|
|
|
|
Set-Content -Path $OutputHtml -Value $html -Encoding UTF8
|
|
Write-Output "AGENTOPS_DASHBOARD_GENERATED: $OutputHtml"
|