@ash-ai/dashboard 0.0.6 → 0.0.8

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.
Files changed (78) hide show
  1. package/app/agents/detail/page.tsx +182 -0
  2. package/app/agents/eval-compare/page.tsx +445 -0
  3. package/app/agents/eval-run/page.tsx +331 -0
  4. package/app/agents/eval-runs/page.tsx +376 -0
  5. package/app/agents/evals/page.tsx +448 -0
  6. package/app/agents/knowledge/page.tsx +202 -0
  7. package/app/agents/page.tsx +5 -3
  8. package/app/agents/versions/page.tsx +280 -0
  9. package/lib/hooks.ts +120 -0
  10. package/out/404/index.html +1 -1
  11. package/out/404.html +1 -1
  12. package/out/_next/static/I4FIhruRCEftVoflyg3Le/_buildManifest.js +1 -0
  13. package/out/_next/static/chunks/839-186b95b4d095e127.js +1 -0
  14. package/out/_next/static/chunks/90-8e47c31ab931867f.js +1 -0
  15. package/out/_next/static/chunks/{929-6faf1adeb65ee383.js → 929-9cd7b87bfba85ee1.js} +1 -1
  16. package/out/_next/static/chunks/app/agents/detail/page-7427483b9c74ad63.js +1 -0
  17. package/out/_next/static/chunks/app/agents/eval-compare/page-bc4e8051ba07e56f.js +1 -0
  18. package/out/_next/static/chunks/app/agents/eval-run/page-a38dac74d1b3787d.js +1 -0
  19. package/out/_next/static/chunks/app/agents/eval-runs/page-af2fb33c0fce7934.js +1 -0
  20. package/out/_next/static/chunks/app/agents/evals/page-6bdc4b839a7a8eda.js +1 -0
  21. package/out/_next/static/chunks/app/agents/knowledge/page-0e02b14bfa2a6d04.js +1 -0
  22. package/out/_next/static/chunks/app/agents/page-99f179eb7c41ebd4.js +1 -0
  23. package/out/_next/static/chunks/app/agents/versions/page-c482d9bad8f35df6.js +1 -0
  24. package/out/_next/static/chunks/app/analytics/page-ca5d8c60e62118ed.js +1 -0
  25. package/out/_next/static/chunks/app/layout-b06d1caafc026d0c.js +1 -0
  26. package/out/_next/static/chunks/app/logs/page-1a7df17a605f36d3.js +1 -0
  27. package/out/_next/static/chunks/app/page-9e02cb0e8897ab5d.js +1 -0
  28. package/out/_next/static/chunks/app/playground/{page-10d3461f118bfb21.js → page-cb17c2ffaeb31b4e.js} +1 -1
  29. package/out/_next/static/chunks/app/queue/page-6013b93817822c75.js +1 -0
  30. package/out/_next/static/chunks/app/sessions/page-add67d96ab66b690.js +1 -0
  31. package/out/_next/static/chunks/app/settings/credentials/page-ffe97ffb2f60229d.js +1 -0
  32. package/out/_next/static/css/ab505eeeff3f7df5.css +1 -0
  33. package/out/agents/detail/index.html +1 -0
  34. package/out/agents/detail/index.txt +22 -0
  35. package/out/agents/eval-compare/index.html +1 -0
  36. package/out/agents/eval-compare/index.txt +22 -0
  37. package/out/agents/eval-run/index.html +1 -0
  38. package/out/agents/eval-run/index.txt +22 -0
  39. package/out/agents/eval-runs/index.html +1 -0
  40. package/out/agents/eval-runs/index.txt +22 -0
  41. package/out/agents/evals/index.html +1 -0
  42. package/out/agents/evals/index.txt +22 -0
  43. package/out/agents/index.html +1 -1
  44. package/out/agents/index.txt +6 -6
  45. package/out/agents/knowledge/index.html +1 -0
  46. package/out/agents/knowledge/index.txt +22 -0
  47. package/out/agents/versions/index.html +1 -0
  48. package/out/agents/versions/index.txt +22 -0
  49. package/out/analytics/index.html +1 -1
  50. package/out/analytics/index.txt +6 -6
  51. package/out/index.html +1 -1
  52. package/out/index.txt +6 -6
  53. package/out/logs/index.html +1 -1
  54. package/out/logs/index.txt +6 -6
  55. package/out/playground/index.html +1 -1
  56. package/out/playground/index.txt +6 -6
  57. package/out/queue/index.html +1 -1
  58. package/out/queue/index.txt +6 -6
  59. package/out/sessions/index.html +1 -1
  60. package/out/sessions/index.txt +6 -6
  61. package/out/settings/api-keys/index.html +1 -1
  62. package/out/settings/api-keys/index.txt +6 -6
  63. package/out/settings/credentials/index.html +1 -1
  64. package/out/settings/credentials/index.txt +6 -6
  65. package/package.json +4 -4
  66. package/out/_next/static/_fEfzU87y-4u457akpPDC/_buildManifest.js +0 -1
  67. package/out/_next/static/chunks/322-bab4df5c5188e993.js +0 -1
  68. package/out/_next/static/chunks/432-11ec8af7ccfbd019.js +0 -1
  69. package/out/_next/static/chunks/app/agents/page-5f872b5fa12d7854.js +0 -1
  70. package/out/_next/static/chunks/app/analytics/page-bb296f848e25a94f.js +0 -1
  71. package/out/_next/static/chunks/app/layout-f5d1d76b525135c7.js +0 -1
  72. package/out/_next/static/chunks/app/logs/page-5165b556d13654ae.js +0 -1
  73. package/out/_next/static/chunks/app/page-d1e6d7bff1216f08.js +0 -1
  74. package/out/_next/static/chunks/app/queue/page-50142f2cfb3664e7.js +0 -1
  75. package/out/_next/static/chunks/app/sessions/page-7f55a0a4ba0be458.js +0 -1
  76. package/out/_next/static/chunks/app/settings/credentials/page-deb5556bfe57b8b9.js +0 -1
  77. package/out/_next/static/css/15bfa5d891bcf58c.css +0 -1
  78. /package/out/_next/static/{_fEfzU87y-4u457akpPDC → I4FIhruRCEftVoflyg3Le}/_ssgManifest.js +0 -0
@@ -0,0 +1,331 @@
1
+ 'use client'
2
+
3
+ import { Suspense, useState, useEffect } from 'react'
4
+ import { useSearchParams } from 'next/navigation'
5
+ import Link from 'next/link'
6
+ import { useEvalRunResults } from '@/lib/hooks'
7
+ import { getClient } from '@/lib/client'
8
+ import { Card, CardContent } from '@/components/ui/card'
9
+ import { Button } from '@/components/ui/button'
10
+ import { Badge } from '@/components/ui/badge'
11
+ import { EmptyState } from '@/components/ui/empty-state'
12
+ import { ShimmerBlock } from '@/components/ui/shimmer'
13
+ import { formatRelativeTime, truncateId } from '@/lib/utils'
14
+ import {
15
+ ArrowLeft,
16
+ BarChart3,
17
+ CheckCircle2,
18
+ XCircle,
19
+ Clock,
20
+ Loader2,
21
+ AlertTriangle,
22
+ ChevronDown,
23
+ ChevronRight,
24
+ } from 'lucide-react'
25
+
26
+ function statusVariant(status: string): 'success' | 'info' | 'error' | 'warning' | 'default' {
27
+ switch (status) {
28
+ case 'completed': return 'success'
29
+ case 'running': return 'info'
30
+ case 'error': return 'error'
31
+ case 'pending': return 'warning'
32
+ default: return 'default'
33
+ }
34
+ }
35
+
36
+ function scoreColor(score: number | null): string {
37
+ if (score === null) return 'text-white/30'
38
+ if (score >= 0.8) return 'text-green-400'
39
+ if (score >= 0.5) return 'text-yellow-400'
40
+ return 'text-red-400'
41
+ }
42
+
43
+ function EvalRunDetailContent() {
44
+ const searchParams = useSearchParams()
45
+ const name = searchParams.get('name')
46
+ const runId = searchParams.get('runId')
47
+ const { results, loading, refresh } = useEvalRunResults(name, runId)
48
+ const [run, setRun] = useState<any>(null)
49
+ const [runLoading, setRunLoading] = useState(true)
50
+ const [expandedResult, setExpandedResult] = useState<string | null>(null)
51
+
52
+ // Fetch the run metadata
53
+ useEffect(() => {
54
+ if (!name || !runId) {
55
+ setRunLoading(false)
56
+ return
57
+ }
58
+ async function fetchRun() {
59
+ try {
60
+ const data = await getClient().getEvalRun(name!, runId!)
61
+ setRun(data)
62
+ } catch (e) {
63
+ console.error('Failed to fetch eval run:', e)
64
+ } finally {
65
+ setRunLoading(false)
66
+ }
67
+ }
68
+ fetchRun()
69
+ }, [name, runId])
70
+
71
+ // Auto-refresh for in-progress runs
72
+ useEffect(() => {
73
+ if (!run) return
74
+ const isInProgress = run.status === 'running' || run.status === 'pending'
75
+ if (!isInProgress) return
76
+ const interval = setInterval(() => {
77
+ refresh()
78
+ // Re-fetch run metadata too
79
+ if (name && runId) {
80
+ getClient().getEvalRun(name, runId).then(setRun).catch(() => {})
81
+ }
82
+ }, 5000)
83
+ return () => clearInterval(interval)
84
+ }, [run, refresh, name, runId])
85
+
86
+ if (!name || !runId) {
87
+ return (
88
+ <div className="text-center py-16">
89
+ <p className="text-white/50">Missing agent name or run ID.</p>
90
+ <Link href="/agents" className="text-indigo-400 hover:text-indigo-300 text-sm mt-2 inline-block">
91
+ Back to agents
92
+ </Link>
93
+ </div>
94
+ )
95
+ }
96
+
97
+ if (runLoading || loading) {
98
+ return (
99
+ <div className="space-y-6">
100
+ <ShimmerBlock height={120} />
101
+ <div className="space-y-3">
102
+ {[1, 2, 3].map((i) => (
103
+ <ShimmerBlock key={i} height={70} />
104
+ ))}
105
+ </div>
106
+ </div>
107
+ )
108
+ }
109
+
110
+ return (
111
+ <div className="space-y-6">
112
+ {/* Back link */}
113
+ <Link
114
+ href={`/agents/eval-runs?name=${encodeURIComponent(name)}`}
115
+ className="inline-flex items-center gap-1.5 text-sm text-white/50 hover:text-white transition-colors"
116
+ >
117
+ <ArrowLeft className="h-4 w-4" />
118
+ Back to eval runs
119
+ </Link>
120
+
121
+ {/* Run Summary */}
122
+ <Card>
123
+ <CardContent>
124
+ <div className="flex items-center justify-between mb-4">
125
+ <div>
126
+ <div className="flex items-center gap-2">
127
+ <h1 className="text-xl font-bold text-white">
128
+ Run {truncateId(runId)}
129
+ </h1>
130
+ {run && (
131
+ <Badge variant={statusVariant(run.status)}>
132
+ {run.status === 'running' && <Loader2 className="h-3 w-3 mr-1 animate-spin" />}
133
+ {run.status}
134
+ </Badge>
135
+ )}
136
+ {run?.versionNumber !== null && run?.versionNumber !== undefined && (
137
+ <Badge variant="default">v{run.versionNumber}</Badge>
138
+ )}
139
+ </div>
140
+ {run && (
141
+ <p className="text-xs text-white/30 mt-1">
142
+ Started {formatRelativeTime(run.createdAt)}
143
+ {run.completedAt && ` | Completed ${formatRelativeTime(run.completedAt)}`}
144
+ </p>
145
+ )}
146
+ </div>
147
+ {run && (
148
+ <div className="text-sm text-white/50">
149
+ {run.completedCases}/{run.totalCases} cases completed
150
+ </div>
151
+ )}
152
+ </div>
153
+
154
+ {/* Summary metrics */}
155
+ {run?.summary && (
156
+ <div className="grid grid-cols-2 sm:grid-cols-5 gap-4 mt-4 pt-4 border-t border-white/5">
157
+ <MetricCard
158
+ label="Pass Rate"
159
+ value={`${(run.summary.passRate * 100).toFixed(0)}%`}
160
+ color={run.summary.passRate >= 0.8 ? 'text-green-400' : run.summary.passRate >= 0.5 ? 'text-yellow-400' : 'text-red-400'}
161
+ />
162
+ <MetricCard
163
+ label="Avg Topic Score"
164
+ value={run.summary.avgTopicScore.toFixed(2)}
165
+ color={scoreColor(run.summary.avgTopicScore)}
166
+ />
167
+ <MetricCard
168
+ label="Avg Safety Score"
169
+ value={run.summary.avgSafetyScore.toFixed(2)}
170
+ color={scoreColor(run.summary.avgSafetyScore)}
171
+ />
172
+ {run.summary.avgLlmJudgeScore !== null && (
173
+ <MetricCard
174
+ label="LLM Judge Score"
175
+ value={run.summary.avgLlmJudgeScore.toFixed(2)}
176
+ color={scoreColor(run.summary.avgLlmJudgeScore)}
177
+ />
178
+ )}
179
+ <MetricCard
180
+ label="Avg Latency"
181
+ value={`${run.summary.avgLatencyMs.toFixed(0)}ms`}
182
+ color="text-white"
183
+ />
184
+ </div>
185
+ )}
186
+ </CardContent>
187
+ </Card>
188
+
189
+ {/* Results Table */}
190
+ <div>
191
+ <h2 className="text-lg font-semibold text-white mb-3">Results</h2>
192
+ {results.length === 0 ? (
193
+ <EmptyState
194
+ icon={<BarChart3 className="h-12 w-12" />}
195
+ title="No results yet"
196
+ description={run?.status === 'pending' || run?.status === 'running'
197
+ ? 'Results will appear as the eval run progresses.'
198
+ : 'No results were recorded for this run.'}
199
+ />
200
+ ) : (
201
+ <div className="space-y-2">
202
+ {results.map((result: any) => {
203
+ const isExpanded = expandedResult === result.id
204
+ return (
205
+ <Card key={result.id}>
206
+ <CardContent className="!py-3">
207
+ <div className="flex items-center justify-between">
208
+ <button
209
+ onClick={() => setExpandedResult(isExpanded ? null : result.id)}
210
+ className="flex items-center gap-2 min-w-0 flex-1 text-left"
211
+ >
212
+ {isExpanded ? (
213
+ <ChevronDown className="h-4 w-4 text-white/40 flex-shrink-0" />
214
+ ) : (
215
+ <ChevronRight className="h-4 w-4 text-white/40 flex-shrink-0" />
216
+ )}
217
+ {result.status === 'completed' ? (
218
+ <CheckCircle2 className="h-4 w-4 text-green-400 flex-shrink-0" />
219
+ ) : result.status === 'error' ? (
220
+ <XCircle className="h-4 w-4 text-red-400 flex-shrink-0" />
221
+ ) : result.status === 'running' ? (
222
+ <Loader2 className="h-4 w-4 text-blue-400 animate-spin flex-shrink-0" />
223
+ ) : (
224
+ <Clock className="h-4 w-4 text-yellow-400 flex-shrink-0" />
225
+ )}
226
+ <span className="text-sm text-white truncate">
227
+ Case {truncateId(result.evalCaseId)}
228
+ </span>
229
+ </button>
230
+ <div className="flex items-center gap-3 flex-shrink-0 ml-2">
231
+ <Badge variant={statusVariant(result.status)}>{result.status}</Badge>
232
+ {result.topicScore !== null && (
233
+ <div className="text-right">
234
+ <div className="text-[10px] text-white/30">Topic</div>
235
+ <div className={`text-xs font-semibold ${scoreColor(result.topicScore)}`}>
236
+ {result.topicScore.toFixed(2)}
237
+ </div>
238
+ </div>
239
+ )}
240
+ {result.safetyScore !== null && (
241
+ <div className="text-right">
242
+ <div className="text-[10px] text-white/30">Safety</div>
243
+ <div className={`text-xs font-semibold ${scoreColor(result.safetyScore)}`}>
244
+ {result.safetyScore.toFixed(2)}
245
+ </div>
246
+ </div>
247
+ )}
248
+ {result.latencyMs !== null && (
249
+ <div className="text-right">
250
+ <div className="text-[10px] text-white/30">Latency</div>
251
+ <div className="text-xs font-semibold text-white">
252
+ {result.latencyMs.toFixed(0)}ms
253
+ </div>
254
+ </div>
255
+ )}
256
+ </div>
257
+ </div>
258
+ {isExpanded && (
259
+ <div className="mt-3 pt-3 border-t border-white/5 space-y-3">
260
+ {result.agentResponse && (
261
+ <div>
262
+ <div className="text-xs font-medium text-white/40 mb-1">Agent Response</div>
263
+ <div className="text-sm text-white/70 bg-black/20 rounded-lg p-3 whitespace-pre-wrap max-h-48 overflow-y-auto">
264
+ {result.agentResponse}
265
+ </div>
266
+ </div>
267
+ )}
268
+ {result.error && (
269
+ <div>
270
+ <div className="text-xs font-medium text-red-400 mb-1">Error</div>
271
+ <div className="text-sm text-red-300 bg-red-500/10 rounded-lg p-3">
272
+ {result.error}
273
+ </div>
274
+ </div>
275
+ )}
276
+ {result.llmJudgeScore !== null && (
277
+ <div className="flex items-center gap-2">
278
+ <span className="text-xs text-white/40">LLM Judge Score:</span>
279
+ <span className={`text-sm font-semibold ${scoreColor(result.llmJudgeScore)}`}>
280
+ {result.llmJudgeScore.toFixed(2)}
281
+ </span>
282
+ </div>
283
+ )}
284
+ {result.humanScore !== null && (
285
+ <div className="flex items-center gap-2">
286
+ <span className="text-xs text-white/40">Human Score:</span>
287
+ <span className="text-sm font-semibold text-white">
288
+ {result.humanScore}
289
+ </span>
290
+ {result.humanNotes && (
291
+ <span className="text-xs text-white/50">- {result.humanNotes}</span>
292
+ )}
293
+ </div>
294
+ )}
295
+ </div>
296
+ )}
297
+ </CardContent>
298
+ </Card>
299
+ )
300
+ })}
301
+ </div>
302
+ )}
303
+ </div>
304
+ </div>
305
+ )
306
+ }
307
+
308
+ function MetricCard({
309
+ label,
310
+ value,
311
+ color,
312
+ }: {
313
+ label: string
314
+ value: string
315
+ color: string
316
+ }) {
317
+ return (
318
+ <div className="text-center">
319
+ <div className="text-xs text-white/40 mb-1">{label}</div>
320
+ <div className={`text-lg font-bold ${color}`}>{value}</div>
321
+ </div>
322
+ )
323
+ }
324
+
325
+ export default function EvalRunPage() {
326
+ return (
327
+ <Suspense fallback={<ShimmerBlock height={200} />}>
328
+ <EvalRunDetailContent />
329
+ </Suspense>
330
+ )
331
+ }