@ash-ai/dashboard 0.0.5 → 0.0.7

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 (79) 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/app/sessions/page.tsx +21 -15
  10. package/lib/hooks.ts +120 -0
  11. package/out/404/index.html +1 -1
  12. package/out/404.html +1 -1
  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-b631fe082fe4e852.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/_next/static/sXYgh3eUKXRKt1T_1T3tk/_buildManifest.js +1 -0
  34. package/out/agents/detail/index.html +1 -0
  35. package/out/agents/detail/index.txt +22 -0
  36. package/out/agents/eval-compare/index.html +1 -0
  37. package/out/agents/eval-compare/index.txt +22 -0
  38. package/out/agents/eval-run/index.html +1 -0
  39. package/out/agents/eval-run/index.txt +22 -0
  40. package/out/agents/eval-runs/index.html +1 -0
  41. package/out/agents/eval-runs/index.txt +22 -0
  42. package/out/agents/evals/index.html +1 -0
  43. package/out/agents/evals/index.txt +22 -0
  44. package/out/agents/index.html +1 -1
  45. package/out/agents/index.txt +6 -6
  46. package/out/agents/knowledge/index.html +1 -0
  47. package/out/agents/knowledge/index.txt +22 -0
  48. package/out/agents/versions/index.html +1 -0
  49. package/out/agents/versions/index.txt +22 -0
  50. package/out/analytics/index.html +1 -1
  51. package/out/analytics/index.txt +6 -6
  52. package/out/index.html +1 -1
  53. package/out/index.txt +6 -6
  54. package/out/logs/index.html +1 -1
  55. package/out/logs/index.txt +6 -6
  56. package/out/playground/index.html +1 -1
  57. package/out/playground/index.txt +6 -6
  58. package/out/queue/index.html +1 -1
  59. package/out/queue/index.txt +6 -6
  60. package/out/sessions/index.html +1 -1
  61. package/out/sessions/index.txt +6 -6
  62. package/out/settings/api-keys/index.html +1 -1
  63. package/out/settings/api-keys/index.txt +6 -6
  64. package/out/settings/credentials/index.html +1 -1
  65. package/out/settings/credentials/index.txt +6 -6
  66. package/package.json +4 -4
  67. package/out/_next/static/ALf6-9rl7RWKPirFYjchn/_buildManifest.js +0 -1
  68. package/out/_next/static/chunks/322-bab4df5c5188e993.js +0 -1
  69. package/out/_next/static/chunks/432-11ec8af7ccfbd019.js +0 -1
  70. package/out/_next/static/chunks/app/agents/page-5f872b5fa12d7854.js +0 -1
  71. package/out/_next/static/chunks/app/analytics/page-bb296f848e25a94f.js +0 -1
  72. package/out/_next/static/chunks/app/layout-f5d1d76b525135c7.js +0 -1
  73. package/out/_next/static/chunks/app/logs/page-5165b556d13654ae.js +0 -1
  74. package/out/_next/static/chunks/app/page-d1e6d7bff1216f08.js +0 -1
  75. package/out/_next/static/chunks/app/queue/page-50142f2cfb3664e7.js +0 -1
  76. package/out/_next/static/chunks/app/sessions/page-2410b352f297ae91.js +0 -1
  77. package/out/_next/static/chunks/app/settings/credentials/page-deb5556bfe57b8b9.js +0 -1
  78. package/out/_next/static/css/15bfa5d891bcf58c.css +0 -1
  79. /package/out/_next/static/{ALf6-9rl7RWKPirFYjchn → sXYgh3eUKXRKt1T_1T3tk}/_ssgManifest.js +0 -0
@@ -0,0 +1,376 @@
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 { useEvalRuns, useAgentVersions } 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 { Select } from '@/components/ui/select'
11
+ import { Badge } from '@/components/ui/badge'
12
+ import { EmptyState } from '@/components/ui/empty-state'
13
+ import { ShimmerBlock } from '@/components/ui/shimmer'
14
+ import { formatRelativeTime, truncateId } from '@/lib/utils'
15
+ import {
16
+ ArrowLeft,
17
+ Play,
18
+ BarChart3,
19
+ Loader2,
20
+ GitCompare,
21
+ CheckCircle2,
22
+ XCircle,
23
+ Clock,
24
+ AlertTriangle,
25
+ X,
26
+ } from 'lucide-react'
27
+
28
+ function statusIcon(status: string) {
29
+ switch (status) {
30
+ case 'completed':
31
+ return <CheckCircle2 className="h-4 w-4 text-green-400" />
32
+ case 'running':
33
+ return <Loader2 className="h-4 w-4 text-blue-400 animate-spin" />
34
+ case 'failed':
35
+ return <XCircle className="h-4 w-4 text-red-400" />
36
+ case 'pending':
37
+ return <Clock className="h-4 w-4 text-yellow-400" />
38
+ default:
39
+ return <AlertTriangle className="h-4 w-4 text-white/40" />
40
+ }
41
+ }
42
+
43
+ function statusVariant(status: string): 'success' | 'info' | 'error' | 'warning' | 'default' {
44
+ switch (status) {
45
+ case 'completed': return 'success'
46
+ case 'running': return 'info'
47
+ case 'failed': return 'error'
48
+ case 'pending': return 'warning'
49
+ default: return 'default'
50
+ }
51
+ }
52
+
53
+ function EvalRunsContent() {
54
+ const searchParams = useSearchParams()
55
+ const name = searchParams.get('name')
56
+ const { runs, loading, refresh } = useEvalRuns(name)
57
+ const { versions } = useAgentVersions(name)
58
+ const [showRunModal, setShowRunModal] = useState(false)
59
+ const [compareMode, setCompareMode] = useState(false)
60
+ const [selectedRuns, setSelectedRuns] = useState<string[]>([])
61
+ const [error, setError] = useState<string | null>(null)
62
+
63
+ // Auto-refresh when there are running runs
64
+ useEffect(() => {
65
+ const hasRunning = runs.some((r: any) => r.status === 'running' || r.status === 'pending')
66
+ if (!hasRunning) return
67
+ const interval = setInterval(() => refresh(), 5000)
68
+ return () => clearInterval(interval)
69
+ }, [runs, refresh])
70
+
71
+ function toggleRunSelection(runId: string) {
72
+ setSelectedRuns((prev) => {
73
+ if (prev.includes(runId)) return prev.filter((id) => id !== runId)
74
+ if (prev.length >= 2) return [prev[1], runId]
75
+ return [...prev, runId]
76
+ })
77
+ }
78
+
79
+ if (!name) {
80
+ return (
81
+ <div className="text-center py-16">
82
+ <p className="text-white/50">No agent name specified.</p>
83
+ <Link href="/agents" className="text-indigo-400 hover:text-indigo-300 text-sm mt-2 inline-block">
84
+ Back to agents
85
+ </Link>
86
+ </div>
87
+ )
88
+ }
89
+
90
+ return (
91
+ <div className="space-y-6">
92
+ {/* Back link */}
93
+ <Link
94
+ href={`/agents/evals?name=${encodeURIComponent(name)}`}
95
+ className="inline-flex items-center gap-1.5 text-sm text-white/50 hover:text-white transition-colors"
96
+ >
97
+ <ArrowLeft className="h-4 w-4" />
98
+ Back to eval cases
99
+ </Link>
100
+
101
+ <div className="flex items-center justify-between">
102
+ <div>
103
+ <h1 className="text-2xl font-bold text-white">Eval Runs</h1>
104
+ <p className="mt-1 text-sm text-white/50">
105
+ Run history for <span className="text-white/70">{name}</span>
106
+ </p>
107
+ </div>
108
+ <div className="flex items-center gap-2">
109
+ {runs.length >= 2 && (
110
+ <Button
111
+ variant={compareMode ? 'primary' : 'secondary'}
112
+ onClick={() => {
113
+ setCompareMode(!compareMode)
114
+ setSelectedRuns([])
115
+ }}
116
+ >
117
+ <GitCompare className="h-4 w-4 mr-2" />
118
+ {compareMode ? 'Cancel Compare' : 'Compare'}
119
+ </Button>
120
+ )}
121
+ {compareMode && selectedRuns.length === 2 && (
122
+ <Link
123
+ href={`/agents/eval-compare?name=${encodeURIComponent(name)}&runA=${selectedRuns[0]}&runB=${selectedRuns[1]}`}
124
+ >
125
+ <Button>
126
+ <BarChart3 className="h-4 w-4 mr-2" />
127
+ Compare Selected
128
+ </Button>
129
+ </Link>
130
+ )}
131
+ {!compareMode && (
132
+ <Button onClick={() => setShowRunModal(true)}>
133
+ <Play className="h-4 w-4 mr-2" />
134
+ Run Evals
135
+ </Button>
136
+ )}
137
+ </div>
138
+ </div>
139
+
140
+ {error && (
141
+ <div className="text-sm text-red-400 bg-red-500/10 border border-red-500/20 rounded-lg px-4 py-2">
142
+ {error}
143
+ </div>
144
+ )}
145
+
146
+ {compareMode && (
147
+ <div className="text-sm text-white/50 bg-indigo-500/5 border border-indigo-500/20 rounded-lg px-4 py-2">
148
+ Select 2 runs to compare. Selected: {selectedRuns.length}/2
149
+ </div>
150
+ )}
151
+
152
+ {loading ? (
153
+ <div className="space-y-3">
154
+ {[1, 2, 3].map((i) => (
155
+ <ShimmerBlock key={i} height={80} />
156
+ ))}
157
+ </div>
158
+ ) : runs.length === 0 ? (
159
+ <EmptyState
160
+ icon={<BarChart3 className="h-12 w-12" />}
161
+ title="No eval runs yet"
162
+ description="Start an eval run to test your agent against the defined eval cases."
163
+ action={
164
+ <Button onClick={() => setShowRunModal(true)}>
165
+ <Play className="h-4 w-4 mr-2" />
166
+ Run Evals
167
+ </Button>
168
+ }
169
+ />
170
+ ) : (
171
+ <div className="space-y-3">
172
+ {runs.map((run: any) => {
173
+ const isSelected = selectedRuns.includes(run.id)
174
+ return (
175
+ <Card
176
+ key={run.id}
177
+ className={compareMode && isSelected ? 'border-indigo-500/50 bg-indigo-500/5' : ''}
178
+ >
179
+ <CardContent>
180
+ <div className="flex items-center justify-between">
181
+ <div className="flex items-center gap-3 min-w-0 flex-1">
182
+ {compareMode && (
183
+ <button
184
+ onClick={() => toggleRunSelection(run.id)}
185
+ className={`flex-shrink-0 h-5 w-5 rounded border-2 transition-colors ${
186
+ isSelected
187
+ ? 'bg-indigo-500 border-indigo-500'
188
+ : 'border-white/20 hover:border-white/40'
189
+ }`}
190
+ >
191
+ {isSelected && <CheckCircle2 className="h-4 w-4 text-white" />}
192
+ </button>
193
+ )}
194
+ {statusIcon(run.status)}
195
+ <div className="min-w-0">
196
+ <div className="flex items-center gap-2">
197
+ {!compareMode ? (
198
+ <Link
199
+ href={`/agents/eval-run?name=${encodeURIComponent(name)}&runId=${run.id}`}
200
+ className="text-sm font-semibold text-white hover:text-indigo-400 transition-colors"
201
+ >
202
+ Run {truncateId(run.id)}
203
+ </Link>
204
+ ) : (
205
+ <span className="text-sm font-semibold text-white">
206
+ Run {truncateId(run.id)}
207
+ </span>
208
+ )}
209
+ <Badge variant={statusVariant(run.status)}>{run.status}</Badge>
210
+ {run.versionNumber !== null && run.versionNumber !== undefined && (
211
+ <Badge variant="default">v{run.versionNumber}</Badge>
212
+ )}
213
+ </div>
214
+ <div className="flex items-center gap-3 mt-1">
215
+ <span className="text-xs text-white/30">
216
+ {formatRelativeTime(run.createdAt)}
217
+ </span>
218
+ <span className="text-xs text-white/30">
219
+ {run.completedCases}/{run.totalCases} cases
220
+ </span>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ {run.summary && (
225
+ <div className="flex items-center gap-4 flex-shrink-0">
226
+ <div className="text-right">
227
+ <div className="text-xs text-white/40">Pass Rate</div>
228
+ <div className={`text-sm font-semibold ${
229
+ run.summary.passRate >= 0.8
230
+ ? 'text-green-400'
231
+ : run.summary.passRate >= 0.5
232
+ ? 'text-yellow-400'
233
+ : 'text-red-400'
234
+ }`}>
235
+ {(run.summary.passRate * 100).toFixed(0)}%
236
+ </div>
237
+ </div>
238
+ <div className="text-right">
239
+ <div className="text-xs text-white/40">Topic</div>
240
+ <div className="text-sm font-semibold text-white">
241
+ {run.summary.avgTopicScore.toFixed(2)}
242
+ </div>
243
+ </div>
244
+ <div className="text-right">
245
+ <div className="text-xs text-white/40">Safety</div>
246
+ <div className="text-sm font-semibold text-white">
247
+ {run.summary.avgSafetyScore.toFixed(2)}
248
+ </div>
249
+ </div>
250
+ <div className="text-right">
251
+ <div className="text-xs text-white/40">Latency</div>
252
+ <div className="text-sm font-semibold text-white">
253
+ {run.summary.avgLatencyMs.toFixed(0)}ms
254
+ </div>
255
+ </div>
256
+ </div>
257
+ )}
258
+ </div>
259
+ </CardContent>
260
+ </Card>
261
+ )
262
+ })}
263
+ </div>
264
+ )}
265
+
266
+ {/* Run Evals Modal */}
267
+ {showRunModal && (
268
+ <RunEvalsModal
269
+ agentName={name}
270
+ versions={versions}
271
+ onClose={() => setShowRunModal(false)}
272
+ onStarted={() => {
273
+ setShowRunModal(false)
274
+ refresh()
275
+ }}
276
+ />
277
+ )}
278
+ </div>
279
+ )
280
+ }
281
+
282
+ // ─── Run Evals Modal ───
283
+
284
+ function RunEvalsModal({
285
+ agentName,
286
+ versions,
287
+ onClose,
288
+ onStarted,
289
+ }: {
290
+ agentName: string
291
+ versions: any[]
292
+ onClose: () => void
293
+ onStarted: () => void
294
+ }) {
295
+ const [versionNumber, setVersionNumber] = useState<string>('')
296
+ const [starting, setStarting] = useState(false)
297
+ const [error, setError] = useState<string | null>(null)
298
+
299
+ async function handleStart() {
300
+ setStarting(true)
301
+ setError(null)
302
+ try {
303
+ await getClient().startEvalRun(agentName, {
304
+ versionNumber: versionNumber ? Number(versionNumber) : undefined,
305
+ })
306
+ onStarted()
307
+ } catch (e) {
308
+ setError(e instanceof Error ? e.message : 'Failed to start eval run')
309
+ } finally {
310
+ setStarting(false)
311
+ }
312
+ }
313
+
314
+ const versionOptions = versions.map((v: any) => ({
315
+ value: String(v.versionNumber),
316
+ label: `v${v.versionNumber}${v.name ? ` - ${v.name}` : ''}${v.isActive ? ' (active)' : ''}`,
317
+ }))
318
+
319
+ return (
320
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
321
+ <Card className="w-full max-w-md">
322
+ <CardContent>
323
+ <div className="flex items-center justify-between mb-6">
324
+ <h2 className="text-lg font-semibold text-white">Run Evals</h2>
325
+ <button onClick={onClose} className="text-white/40 hover:text-white">
326
+ <X className="h-5 w-5" />
327
+ </button>
328
+ </div>
329
+
330
+ <div className="space-y-4">
331
+ <Select
332
+ label="Version (optional)"
333
+ placeholder="Use active version"
334
+ options={versionOptions}
335
+ value={versionNumber}
336
+ onChange={(e) => setVersionNumber(e.target.value)}
337
+ />
338
+
339
+ <p className="text-xs text-white/40">
340
+ This will run all active eval cases against the selected version of the agent.
341
+ </p>
342
+
343
+ {error && <p className="text-sm text-red-400">{error}</p>}
344
+
345
+ <div className="flex justify-end gap-3 pt-2">
346
+ <Button variant="ghost" onClick={onClose}>
347
+ Cancel
348
+ </Button>
349
+ <Button onClick={handleStart} disabled={starting}>
350
+ {starting ? (
351
+ <>
352
+ <Loader2 className="h-4 w-4 mr-2 animate-spin" />
353
+ Starting...
354
+ </>
355
+ ) : (
356
+ <>
357
+ <Play className="h-4 w-4 mr-2" />
358
+ Start Run
359
+ </>
360
+ )}
361
+ </Button>
362
+ </div>
363
+ </div>
364
+ </CardContent>
365
+ </Card>
366
+ </div>
367
+ )
368
+ }
369
+
370
+ export default function EvalRunsPage() {
371
+ return (
372
+ <Suspense fallback={<ShimmerBlock height={200} />}>
373
+ <EvalRunsContent />
374
+ </Suspense>
375
+ )
376
+ }