@bahulam/code 2.6.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/README.md +80 -0
- package/package.json +49 -0
- package/pulse/app/activity/page.tsx +190 -0
- package/pulse/app/api/activity/route.ts +138 -0
- package/pulse/app/api/benchmark/route.ts +113 -0
- package/pulse/app/api/benchmarks/route.ts +195 -0
- package/pulse/app/api/costs/route.ts +88 -0
- package/pulse/app/api/export/route.ts +77 -0
- package/pulse/app/api/history/route.ts +11 -0
- package/pulse/app/api/import/route.ts +31 -0
- package/pulse/app/api/memory/route.ts +50 -0
- package/pulse/app/api/plans/route.ts +9 -0
- package/pulse/app/api/projects/[slug]/route.ts +96 -0
- package/pulse/app/api/projects/route.ts +121 -0
- package/pulse/app/api/sessions/[id]/replay/route.ts +20 -0
- package/pulse/app/api/sessions/[id]/route.ts +31 -0
- package/pulse/app/api/sessions/route.ts +112 -0
- package/pulse/app/api/settings/route.ts +14 -0
- package/pulse/app/api/stats/route.ts +143 -0
- package/pulse/app/api/todos/route.ts +9 -0
- package/pulse/app/api/tools/route.ts +160 -0
- package/pulse/app/benchmarks/page.tsx +224 -0
- package/pulse/app/costs/page.tsx +179 -0
- package/pulse/app/export/page.tsx +465 -0
- package/pulse/app/favicon.ico +0 -0
- package/pulse/app/globals.css +263 -0
- package/pulse/app/help/page.tsx +143 -0
- package/pulse/app/history/page.tsx +157 -0
- package/pulse/app/layout.tsx +46 -0
- package/pulse/app/memory/page.tsx +365 -0
- package/pulse/app/overview-client.tsx +393 -0
- package/pulse/app/page.tsx +14 -0
- package/pulse/app/plans/page.tsx +308 -0
- package/pulse/app/projects/[slug]/page.tsx +390 -0
- package/pulse/app/projects/page.tsx +110 -0
- package/pulse/app/sessions/[id]/page.tsx +243 -0
- package/pulse/app/sessions/page.tsx +39 -0
- package/pulse/app/settings/page.tsx +188 -0
- package/pulse/app/todos/page.tsx +211 -0
- package/pulse/app/tools/page.tsx +249 -0
- package/pulse/cli.js +164 -0
- package/pulse/components/activity/day-of-week-chart.tsx +35 -0
- package/pulse/components/activity/streak-card.tsx +36 -0
- package/pulse/components/costs/cache-efficiency-panel.tsx +76 -0
- package/pulse/components/costs/cost-by-project-chart.tsx +48 -0
- package/pulse/components/costs/cost-over-time-chart.tsx +95 -0
- package/pulse/components/costs/model-token-table.tsx +60 -0
- package/pulse/components/global-search.tsx +193 -0
- package/pulse/components/keyboard-nav-provider.tsx +23 -0
- package/pulse/components/layout/bottom-nav.tsx +53 -0
- package/pulse/components/layout/client-layout.tsx +31 -0
- package/pulse/components/layout/sidebar-context.tsx +50 -0
- package/pulse/components/layout/sidebar.tsx +183 -0
- package/pulse/components/layout/top-bar.tsx +121 -0
- package/pulse/components/overview/activity-heatmap.tsx +107 -0
- package/pulse/components/overview/conversation-table.tsx +148 -0
- package/pulse/components/overview/model-breakdown-donut.tsx +95 -0
- package/pulse/components/overview/peak-hours-chart.tsx +87 -0
- package/pulse/components/overview/project-activity-donut.tsx +96 -0
- package/pulse/components/overview/stat-card.tsx +102 -0
- package/pulse/components/overview/usage-over-time-chart.tsx +166 -0
- package/pulse/components/projects/project-card.tsx +175 -0
- package/pulse/components/sessions/replay/assistant-markdown.tsx +94 -0
- package/pulse/components/sessions/replay/compaction-card.tsx +25 -0
- package/pulse/components/sessions/replay/session-sidebar.tsx +231 -0
- package/pulse/components/sessions/replay/token-accumulation-chart.tsx +98 -0
- package/pulse/components/sessions/replay/tool-call-badge.tsx +127 -0
- package/pulse/components/sessions/replay/turn-cards.tsx +220 -0
- package/pulse/components/sessions/replay/user-tool-result.tsx +158 -0
- package/pulse/components/sessions/session-badges.tsx +49 -0
- package/pulse/components/sessions/session-table.tsx +299 -0
- package/pulse/components/theme-provider.tsx +44 -0
- package/pulse/components/tools/feature-adoption-table.tsx +58 -0
- package/pulse/components/tools/mcp-server-panel.tsx +45 -0
- package/pulse/components/tools/tool-ranking-chart.tsx +57 -0
- package/pulse/components/tools/version-history-table.tsx +32 -0
- package/pulse/components/ui/alert.tsx +66 -0
- package/pulse/components/ui/badge.tsx +48 -0
- package/pulse/components/ui/breadcrumb.tsx +109 -0
- package/pulse/components/ui/button.tsx +64 -0
- package/pulse/components/ui/calendar.tsx +220 -0
- package/pulse/components/ui/card.tsx +92 -0
- package/pulse/components/ui/command.tsx +158 -0
- package/pulse/components/ui/dialog.tsx +158 -0
- package/pulse/components/ui/input.tsx +21 -0
- package/pulse/components/ui/popover.tsx +89 -0
- package/pulse/components/ui/progress.tsx +31 -0
- package/pulse/components/ui/select.tsx +190 -0
- package/pulse/components/ui/separator.tsx +28 -0
- package/pulse/components/ui/sheet.tsx +143 -0
- package/pulse/components/ui/skeleton.tsx +13 -0
- package/pulse/components/ui/table.tsx +116 -0
- package/pulse/components/ui/tabs.tsx +91 -0
- package/pulse/components/ui/tooltip.tsx +57 -0
- package/pulse/components/use-global-keyboard-nav.ts +79 -0
- package/pulse/components.json +23 -0
- package/pulse/eslint.config.mjs +18 -0
- package/pulse/lib/bahulam-paths.ts +23 -0
- package/pulse/lib/claude-reader.ts +592 -0
- package/pulse/lib/decode.ts +129 -0
- package/pulse/lib/pricing.ts +102 -0
- package/pulse/lib/replay-parser.ts +165 -0
- package/pulse/lib/tool-categories.ts +127 -0
- package/pulse/lib/utils.ts +6 -0
- package/pulse/next-env.d.ts +6 -0
- package/pulse/next.config.ts +16 -0
- package/pulse/package.json +45 -0
- package/pulse/postcss.config.mjs +7 -0
- package/pulse/public/activity.png +0 -0
- package/pulse/public/cc-lens.png +0 -0
- package/pulse/public/command-k.png +0 -0
- package/pulse/public/costs.png +0 -0
- package/pulse/public/dashboard-dark.png +0 -0
- package/pulse/public/dashboard-white.png +0 -0
- package/pulse/public/export.png +0 -0
- package/pulse/public/file.svg +1 -0
- package/pulse/public/globe.svg +1 -0
- package/pulse/public/next.svg +1 -0
- package/pulse/public/projects.png +0 -0
- package/pulse/public/session-chat.png +0 -0
- package/pulse/public/todos.png +0 -0
- package/pulse/public/tools.png +0 -0
- package/pulse/public/vercel.svg +1 -0
- package/pulse/public/window.svg +1 -0
- package/pulse/tsconfig.json +34 -0
- package/pulse/types/claude.ts +294 -0
- package/src/agents/loader.mjs +94 -0
- package/src/agents/multi_workflow_loader.mjs +330 -0
- package/src/agents/parser.mjs +205 -0
- package/src/agents/scaffold.mjs +222 -0
- package/src/agents/teams.mjs +123 -0
- package/src/agents/workflow_loader.mjs +122 -0
- package/src/agents/workflow_scaffold.mjs +249 -0
- package/src/auth/oauth.mjs +220 -0
- package/src/auth/tarang-auth.mjs +306 -0
- package/src/commands/agent.mjs +220 -0
- package/src/commands/workflow.mjs +581 -0
- package/src/config/cli-args.mjs +200 -0
- package/src/config/env.mjs +263 -0
- package/src/config/hook-runner.mjs +100 -0
- package/src/config/memory-loader.mjs +32 -0
- package/src/config/settings-loader.mjs +45 -0
- package/src/config/settings.mjs +132 -0
- package/src/context/ast-parser.mjs +298 -0
- package/src/context/bm25.mjs +85 -0
- package/src/context/retriever.mjs +308 -0
- package/src/context/skeleton.mjs +134 -0
- package/src/context/symbol-indexer.mjs +375 -0
- package/src/core/agent-history.mjs +111 -0
- package/src/core/agent-loop.mjs +486 -0
- package/src/core/approval-log.mjs +104 -0
- package/src/core/approval.mjs +476 -0
- package/src/core/attachments.mjs +380 -0
- package/src/core/backend-url.mjs +55 -0
- package/src/core/cache-control.mjs +92 -0
- package/src/core/cache.mjs +105 -0
- package/src/core/callback-client.mjs +180 -0
- package/src/core/checkpoints.mjs +142 -0
- package/src/core/compact-history.mjs +127 -0
- package/src/core/context-envelope.mjs +54 -0
- package/src/core/context-manager.mjs +198 -0
- package/src/core/error-guidance.mjs +311 -0
- package/src/core/file-diff.mjs +217 -0
- package/src/core/headless.mjs +448 -0
- package/src/core/hooks-manager.mjs +87 -0
- package/src/core/jsonl-writer.mjs +449 -0
- package/src/core/local-agent.mjs +537 -0
- package/src/core/local-store.mjs +836 -0
- package/src/core/mode-selector.mjs +51 -0
- package/src/core/output-filter.mjs +177 -0
- package/src/core/paths.mjs +190 -0
- package/src/core/policy-resolver.mjs +156 -0
- package/src/core/pricing.mjs +336 -0
- package/src/core/project-artifacts.mjs +39 -0
- package/src/core/project-context-loader.mjs +139 -0
- package/src/core/providers.mjs +219 -0
- package/src/core/rate-limit-display.mjs +121 -0
- package/src/core/rate-limiter.mjs +119 -0
- package/src/core/resume-mode.mjs +192 -0
- package/src/core/risk-tier.mjs +337 -0
- package/src/core/safety.mjs +203 -0
- package/src/core/scheduler.mjs +173 -0
- package/src/core/session-manager.mjs +360 -0
- package/src/core/session.mjs +143 -0
- package/src/core/settings-sync.mjs +85 -0
- package/src/core/stagnation.mjs +57 -0
- package/src/core/stream-client.mjs +829 -0
- package/src/core/streaming.mjs +182 -0
- package/src/core/system-prompt.mjs +140 -0
- package/src/core/tasks.mjs +196 -0
- package/src/core/tool-executor.mjs +1950 -0
- package/src/core/trust.mjs +158 -0
- package/src/core/work-scope.mjs +248 -0
- package/src/hooks/engine.mjs +162 -0
- package/src/index.mjs +426 -0
- package/src/mcp/client.mjs +253 -0
- package/src/mcp/transport-shttp.mjs +130 -0
- package/src/mcp/transport-sse.mjs +131 -0
- package/src/mcp/transport-ws.mjs +134 -0
- package/src/onboarding/preflight.mjs +360 -0
- package/src/permissions/checker.mjs +57 -0
- package/src/permissions/command-classifier.mjs +652 -0
- package/src/permissions/injection-check.mjs +60 -0
- package/src/permissions/path-check.mjs +102 -0
- package/src/permissions/prompt.mjs +73 -0
- package/src/permissions/sandbox.mjs +112 -0
- package/src/plugins/loader.mjs +138 -0
- package/src/skills/installer.mjs +188 -0
- package/src/skills/loader.mjs +252 -0
- package/src/skills/runner.mjs +55 -0
- package/src/state/orbit.mjs +263 -0
- package/src/state/verbosity.mjs +99 -0
- package/src/telemetry/index.mjs +96 -0
- package/src/terminal/agents.mjs +177 -0
- package/src/terminal/analytics.mjs +292 -0
- package/src/terminal/ansi.mjs +695 -0
- package/src/terminal/init.mjs +145 -0
- package/src/terminal/main.mjs +269 -0
- package/src/terminal/repl-explore.mjs +35 -0
- package/src/terminal/repl-format.mjs +257 -0
- package/src/terminal/repl-render.mjs +561 -0
- package/src/terminal/repl-resume.mjs +625 -0
- package/src/terminal/repl-state.mjs +103 -0
- package/src/terminal/repl-utils.mjs +34 -0
- package/src/terminal/repl.mjs +3832 -0
- package/src/terminal/skills.mjs +54 -0
- package/src/terminal/tool-display.mjs +240 -0
- package/src/tools/agent.mjs +137 -0
- package/src/tools/ask-user.mjs +61 -0
- package/src/tools/bash.mjs +231 -0
- package/src/tools/cron-create.mjs +120 -0
- package/src/tools/cron-delete.mjs +49 -0
- package/src/tools/cron-list.mjs +37 -0
- package/src/tools/edit.mjs +82 -0
- package/src/tools/enter-worktree.mjs +69 -0
- package/src/tools/exit-worktree.mjs +57 -0
- package/src/tools/glob.mjs +117 -0
- package/src/tools/grep.mjs +129 -0
- package/src/tools/lint.mjs +71 -0
- package/src/tools/ls.mjs +58 -0
- package/src/tools/lsp.mjs +115 -0
- package/src/tools/multi-edit.mjs +94 -0
- package/src/tools/notebook-edit.mjs +96 -0
- package/src/tools/project-overview.mjs +641 -0
- package/src/tools/read-mcp-resource.mjs +57 -0
- package/src/tools/read.mjs +138 -0
- package/src/tools/registry.mjs +116 -0
- package/src/tools/remote-trigger.mjs +84 -0
- package/src/tools/send-message.mjs +64 -0
- package/src/tools/skill.mjs +52 -0
- package/src/tools/test-runner.mjs +49 -0
- package/src/tools/todo-write.mjs +68 -0
- package/src/tools/tool-search.mjs +77 -0
- package/src/tools/web-fetch.mjs +65 -0
- package/src/tools/web-search.mjs +89 -0
- package/src/tools/write.mjs +55 -0
- package/src/ui/approval.mjs +263 -0
- package/src/ui/banner.mjs +235 -0
- package/src/ui/commands.mjs +537 -0
- package/src/ui/formatter.mjs +409 -0
- package/src/ui/icons.mjs +164 -0
- package/src/ui/input-dock.mjs +444 -0
- package/src/ui/markdown.mjs +278 -0
- package/src/ui/mission-report.mjs +296 -0
- package/src/ui/palette.mjs +189 -0
- package/src/ui/slash-commands.mjs +245 -0
- package/src/ui/spinner.mjs +116 -0
- package/src/ui/sub-agent.mjs +152 -0
- package/src/ui/term.mjs +159 -0
- package/src/ui/text-layout.mjs +127 -0
- package/src/ui/tool-card.mjs +463 -0
- package/src/ui/tool-details.mjs +312 -0
- package/src/ui/transcript-block.mjs +21 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
5
|
+
import { Badge } from '@/components/ui/badge'
|
|
6
|
+
|
|
7
|
+
interface BenchmarkStats {
|
|
8
|
+
total_runs: number
|
|
9
|
+
passed: number
|
|
10
|
+
failed: number
|
|
11
|
+
error: number
|
|
12
|
+
success_rate: number
|
|
13
|
+
avg_duration: number
|
|
14
|
+
total_cost: number
|
|
15
|
+
total_tokens: number
|
|
16
|
+
avg_tokens_per_run: number
|
|
17
|
+
by_status: Record<string, number>
|
|
18
|
+
by_repo: Record<string, { count: number; passed: number; success_rate: number }>
|
|
19
|
+
by_model: Record<string, { count: number; passed: number; success_rate: number }>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface BenchmarkResponse {
|
|
23
|
+
stats: BenchmarkStats
|
|
24
|
+
filters: {
|
|
25
|
+
repo: string | null
|
|
26
|
+
model: string | null
|
|
27
|
+
status: string | null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function BenchmarksPage() {
|
|
32
|
+
const [data, setData] = useState<BenchmarkResponse | null>(null)
|
|
33
|
+
const [loading, setLoading] = useState(true)
|
|
34
|
+
const [error, setError] = useState<string | null>(null)
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const fetchBenchmarks = async () => {
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch('/api/benchmarks')
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error('Failed to fetch benchmarks')
|
|
42
|
+
}
|
|
43
|
+
const json = await response.json()
|
|
44
|
+
setData(json)
|
|
45
|
+
} catch (err) {
|
|
46
|
+
setError(err instanceof Error ? err.message : 'Unknown error')
|
|
47
|
+
} finally {
|
|
48
|
+
setLoading(false)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fetchBenchmarks()
|
|
53
|
+
}, [])
|
|
54
|
+
|
|
55
|
+
if (loading) {
|
|
56
|
+
return (
|
|
57
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
58
|
+
<p className="text-muted-foreground">Loading benchmarks...</p>
|
|
59
|
+
</div>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (error) {
|
|
64
|
+
return (
|
|
65
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
66
|
+
<p className="text-destructive">Error: {error}</p>
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!data) {
|
|
72
|
+
return (
|
|
73
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
74
|
+
<p className="text-muted-foreground">No benchmark data available</p>
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const stats = data.stats
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className="space-y-6 p-6">
|
|
83
|
+
<div>
|
|
84
|
+
<h1 className="text-3xl font-bold tracking-tight">Benchmarks</h1>
|
|
85
|
+
<p className="text-muted-foreground mt-2">SWE-Bench v4 Flash 300 Results</p>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
{/* Key Metrics */}
|
|
89
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
90
|
+
<Card>
|
|
91
|
+
<CardHeader className="pb-2">
|
|
92
|
+
<CardTitle className="text-sm font-medium">Total Runs</CardTitle>
|
|
93
|
+
</CardHeader>
|
|
94
|
+
<CardContent>
|
|
95
|
+
<div className="text-2xl font-bold">{stats.total_runs}</div>
|
|
96
|
+
</CardContent>
|
|
97
|
+
</Card>
|
|
98
|
+
|
|
99
|
+
<Card>
|
|
100
|
+
<CardHeader className="pb-2">
|
|
101
|
+
<CardTitle className="text-sm font-medium">Success Rate</CardTitle>
|
|
102
|
+
</CardHeader>
|
|
103
|
+
<CardContent>
|
|
104
|
+
<div className="text-2xl font-bold">{stats.success_rate.toFixed(1)}%</div>
|
|
105
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
106
|
+
{stats.passed} passed, {stats.failed} failed
|
|
107
|
+
</p>
|
|
108
|
+
</CardContent>
|
|
109
|
+
</Card>
|
|
110
|
+
|
|
111
|
+
<Card>
|
|
112
|
+
<CardHeader className="pb-2">
|
|
113
|
+
<CardTitle className="text-sm font-medium">Total Cost</CardTitle>
|
|
114
|
+
</CardHeader>
|
|
115
|
+
<CardContent>
|
|
116
|
+
<div className="text-2xl font-bold">${stats.total_cost.toFixed(2)}</div>
|
|
117
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
118
|
+
{stats.avg_tokens_per_run.toFixed(0)} tokens/run
|
|
119
|
+
</p>
|
|
120
|
+
</CardContent>
|
|
121
|
+
</Card>
|
|
122
|
+
|
|
123
|
+
<Card>
|
|
124
|
+
<CardHeader className="pb-2">
|
|
125
|
+
<CardTitle className="text-sm font-medium">Avg Duration</CardTitle>
|
|
126
|
+
</CardHeader>
|
|
127
|
+
<CardContent>
|
|
128
|
+
<div className="text-2xl font-bold">{stats.avg_duration.toFixed(1)}s</div>
|
|
129
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
130
|
+
{(stats.total_tokens / 1000).toFixed(1)}K tokens total
|
|
131
|
+
</p>
|
|
132
|
+
</CardContent>
|
|
133
|
+
</Card>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
{/* Status Breakdown */}
|
|
137
|
+
<Card>
|
|
138
|
+
<CardHeader>
|
|
139
|
+
<CardTitle>Status Breakdown</CardTitle>
|
|
140
|
+
<CardDescription>Distribution of run statuses</CardDescription>
|
|
141
|
+
</CardHeader>
|
|
142
|
+
<CardContent>
|
|
143
|
+
<div className="space-y-3">
|
|
144
|
+
{Object.entries(stats.by_status).map(([status, count]) => (
|
|
145
|
+
<div key={status} className="flex items-center justify-between">
|
|
146
|
+
<div className="flex items-center gap-2">
|
|
147
|
+
<Badge
|
|
148
|
+
variant={
|
|
149
|
+
status === 'success'
|
|
150
|
+
? 'default'
|
|
151
|
+
: status === 'failed'
|
|
152
|
+
? 'destructive'
|
|
153
|
+
: 'secondary'
|
|
154
|
+
}
|
|
155
|
+
>
|
|
156
|
+
{status}
|
|
157
|
+
</Badge>
|
|
158
|
+
<span className="text-sm text-muted-foreground">{count} runs</span>
|
|
159
|
+
</div>
|
|
160
|
+
<span className="text-sm font-medium">
|
|
161
|
+
{((count / stats.total_runs) * 100).toFixed(1)}%
|
|
162
|
+
</span>
|
|
163
|
+
</div>
|
|
164
|
+
))}
|
|
165
|
+
</div>
|
|
166
|
+
</CardContent>
|
|
167
|
+
</Card>
|
|
168
|
+
|
|
169
|
+
{/* By Repository */}
|
|
170
|
+
<Card>
|
|
171
|
+
<CardHeader>
|
|
172
|
+
<CardTitle>Performance by Repository</CardTitle>
|
|
173
|
+
<CardDescription>Success rate and run count per repository</CardDescription>
|
|
174
|
+
</CardHeader>
|
|
175
|
+
<CardContent>
|
|
176
|
+
<div className="space-y-4">
|
|
177
|
+
{Object.entries(stats.by_repo)
|
|
178
|
+
.sort((a, b) => b[1].count - a[1].count)
|
|
179
|
+
.map(([repo, data]) => (
|
|
180
|
+
<div key={repo} className="flex items-center justify-between border-b pb-3 last:border-0">
|
|
181
|
+
<div>
|
|
182
|
+
<p className="font-medium text-sm">{repo}</p>
|
|
183
|
+
<p className="text-xs text-muted-foreground">
|
|
184
|
+
{data.count} runs, {data.passed} passed
|
|
185
|
+
</p>
|
|
186
|
+
</div>
|
|
187
|
+
<div className="text-right">
|
|
188
|
+
<p className="font-bold text-sm">{data.success_rate.toFixed(1)}%</p>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
))}
|
|
192
|
+
</div>
|
|
193
|
+
</CardContent>
|
|
194
|
+
</Card>
|
|
195
|
+
|
|
196
|
+
{/* By Model */}
|
|
197
|
+
<Card>
|
|
198
|
+
<CardHeader>
|
|
199
|
+
<CardTitle>Performance by Model</CardTitle>
|
|
200
|
+
<CardDescription>Success rate and run count per model</CardDescription>
|
|
201
|
+
</CardHeader>
|
|
202
|
+
<CardContent>
|
|
203
|
+
<div className="space-y-4">
|
|
204
|
+
{Object.entries(stats.by_model)
|
|
205
|
+
.sort((a, b) => b[1].count - a[1].count)
|
|
206
|
+
.map(([model, data]) => (
|
|
207
|
+
<div key={model} className="flex items-center justify-between border-b pb-3 last:border-0">
|
|
208
|
+
<div>
|
|
209
|
+
<p className="font-medium text-sm">{model}</p>
|
|
210
|
+
<p className="text-xs text-muted-foreground">
|
|
211
|
+
{data.count} runs, {data.passed} passed
|
|
212
|
+
</p>
|
|
213
|
+
</div>
|
|
214
|
+
<div className="text-right">
|
|
215
|
+
<p className="font-bold text-sm">{data.success_rate.toFixed(1)}%</p>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
))}
|
|
219
|
+
</div>
|
|
220
|
+
</CardContent>
|
|
221
|
+
</Card>
|
|
222
|
+
</div>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import useSWR from 'swr'
|
|
4
|
+
import { TopBar } from '@/components/layout/top-bar'
|
|
5
|
+
import { CostOverTimeChart } from '@/components/costs/cost-over-time-chart'
|
|
6
|
+
import { CostByProjectChart } from '@/components/costs/cost-by-project-chart'
|
|
7
|
+
import { ModelTokenTable } from '@/components/costs/model-token-table'
|
|
8
|
+
import { CacheEfficiencyPanel } from '@/components/costs/cache-efficiency-panel'
|
|
9
|
+
import { formatCost, formatTokens } from '@/lib/decode'
|
|
10
|
+
import { PRICING } from '@/lib/pricing'
|
|
11
|
+
import type { CostAnalytics } from '@/types/claude'
|
|
12
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
13
|
+
import { Skeleton } from '@/components/ui/skeleton'
|
|
14
|
+
import { Alert, AlertDescription } from '@/components/ui/alert'
|
|
15
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
|
16
|
+
import { AlertTriangle, TrendingDown, DollarSign, Banknote } from 'lucide-react'
|
|
17
|
+
|
|
18
|
+
const fetcher = (url: string) =>
|
|
19
|
+
fetch(url).then(r => { if (!r.ok) throw new Error(`API error ${r.status}`); return r.json() })
|
|
20
|
+
|
|
21
|
+
export default function CostsPage() {
|
|
22
|
+
const { data, error, isLoading } = useSWR<CostAnalytics>('/api/costs', fetcher, { refreshInterval: 5_000 })
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="flex flex-col min-h-screen">
|
|
26
|
+
<TopBar title="Costs" subtitle="Estimated spend from ~/.bahulam/" />
|
|
27
|
+
<div className="p-6 space-y-6">
|
|
28
|
+
|
|
29
|
+
{error && (
|
|
30
|
+
<Alert variant="destructive">
|
|
31
|
+
<AlertTriangle className="h-4 w-4" />
|
|
32
|
+
<AlertDescription>Error loading data: {String(error)}</AlertDescription>
|
|
33
|
+
</Alert>
|
|
34
|
+
)}
|
|
35
|
+
|
|
36
|
+
{isLoading && (
|
|
37
|
+
<div className="space-y-4">
|
|
38
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
39
|
+
{Array.from({ length: 3 }).map((_, i) => <Skeleton key={i} className="h-28 rounded-xl" />)}
|
|
40
|
+
</div>
|
|
41
|
+
{Array.from({ length: 3 }).map((_, i) => <Skeleton key={i} className="h-56 rounded-xl" />)}
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
{data && (
|
|
46
|
+
<>
|
|
47
|
+
{/* Hero stat cards */}
|
|
48
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
49
|
+
<Card>
|
|
50
|
+
<CardHeader className="pb-2">
|
|
51
|
+
<CardDescription className="flex items-center gap-2">
|
|
52
|
+
<DollarSign className="w-4 h-4" /> Total Estimated Cost
|
|
53
|
+
</CardDescription>
|
|
54
|
+
<CardTitle className="text-3xl font-bold tabular-nums text-[#d97706]">
|
|
55
|
+
{formatCost(data.total_cost)}
|
|
56
|
+
</CardTitle>
|
|
57
|
+
</CardHeader>
|
|
58
|
+
<CardContent>
|
|
59
|
+
<p className="text-xs text-muted-foreground">All time spend across all projects</p>
|
|
60
|
+
</CardContent>
|
|
61
|
+
</Card>
|
|
62
|
+
|
|
63
|
+
<Card>
|
|
64
|
+
<CardHeader className="pb-2">
|
|
65
|
+
<CardDescription className="flex items-center gap-2">
|
|
66
|
+
<TrendingDown className="w-4 h-4" /> Cache Savings
|
|
67
|
+
</CardDescription>
|
|
68
|
+
<CardTitle className="text-3xl font-bold tabular-nums text-[#34d399]">
|
|
69
|
+
{formatCost(data.total_savings)}
|
|
70
|
+
</CardTitle>
|
|
71
|
+
</CardHeader>
|
|
72
|
+
<CardContent>
|
|
73
|
+
<p className="text-xs text-muted-foreground">Saved by prompt caching</p>
|
|
74
|
+
</CardContent>
|
|
75
|
+
</Card>
|
|
76
|
+
|
|
77
|
+
<Card>
|
|
78
|
+
<CardHeader className="pb-2">
|
|
79
|
+
<CardDescription className="flex items-center gap-2">
|
|
80
|
+
<Banknote className="w-4 h-4" /> Without Cache
|
|
81
|
+
</CardDescription>
|
|
82
|
+
<CardTitle className="text-3xl font-bold tabular-nums text-red-400">
|
|
83
|
+
{formatCost(data.total_cost + data.total_savings)}
|
|
84
|
+
</CardTitle>
|
|
85
|
+
</CardHeader>
|
|
86
|
+
<CardContent>
|
|
87
|
+
<p className="text-xs text-muted-foreground">What you would have spent</p>
|
|
88
|
+
</CardContent>
|
|
89
|
+
</Card>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
{/* Cost over time */}
|
|
93
|
+
{data.daily.length > 0 && (
|
|
94
|
+
<Card>
|
|
95
|
+
<CardHeader>
|
|
96
|
+
<CardTitle>Cost Over Time</CardTitle>
|
|
97
|
+
<CardDescription>Daily estimated spend</CardDescription>
|
|
98
|
+
</CardHeader>
|
|
99
|
+
<CardContent>
|
|
100
|
+
<CostOverTimeChart daily={data.daily} />
|
|
101
|
+
</CardContent>
|
|
102
|
+
</Card>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
{/* Cost by project */}
|
|
106
|
+
{data.by_project.length > 0 && (
|
|
107
|
+
<Card>
|
|
108
|
+
<CardHeader>
|
|
109
|
+
<CardTitle>Cost by Project</CardTitle>
|
|
110
|
+
<CardDescription>Spend breakdown across projects</CardDescription>
|
|
111
|
+
</CardHeader>
|
|
112
|
+
<CardContent>
|
|
113
|
+
<CostByProjectChart projects={data.by_project} />
|
|
114
|
+
</CardContent>
|
|
115
|
+
</Card>
|
|
116
|
+
)}
|
|
117
|
+
|
|
118
|
+
{/* Per-model table */}
|
|
119
|
+
<Card>
|
|
120
|
+
<CardHeader>
|
|
121
|
+
<CardTitle>Per-Model Token Breakdown</CardTitle>
|
|
122
|
+
<CardDescription>Token usage and cost by model</CardDescription>
|
|
123
|
+
</CardHeader>
|
|
124
|
+
<CardContent>
|
|
125
|
+
<ModelTokenTable models={data.models} />
|
|
126
|
+
</CardContent>
|
|
127
|
+
</Card>
|
|
128
|
+
|
|
129
|
+
{/* Cache efficiency */}
|
|
130
|
+
<Card>
|
|
131
|
+
<CardHeader>
|
|
132
|
+
<CardTitle>Cache Efficiency</CardTitle>
|
|
133
|
+
<CardDescription>How much caching is saving you</CardDescription>
|
|
134
|
+
</CardHeader>
|
|
135
|
+
<CardContent>
|
|
136
|
+
<CacheEfficiencyPanel models={data.models} totalSavings={data.total_savings} />
|
|
137
|
+
</CardContent>
|
|
138
|
+
</Card>
|
|
139
|
+
|
|
140
|
+
{/* Pricing reference */}
|
|
141
|
+
<Card>
|
|
142
|
+
<CardHeader>
|
|
143
|
+
<CardTitle>Pricing Reference</CardTitle>
|
|
144
|
+
<CardDescription>
|
|
145
|
+
Estimates only — update rates in{' '}
|
|
146
|
+
<code className="text-xs bg-muted px-1 rounded">lib/pricing.ts</code>
|
|
147
|
+
</CardDescription>
|
|
148
|
+
</CardHeader>
|
|
149
|
+
<CardContent>
|
|
150
|
+
<Table>
|
|
151
|
+
<TableHeader>
|
|
152
|
+
<TableRow>
|
|
153
|
+
<TableHead>Model</TableHead>
|
|
154
|
+
<TableHead className="text-right">Input /MTok</TableHead>
|
|
155
|
+
<TableHead className="text-right">Output /MTok</TableHead>
|
|
156
|
+
<TableHead className="text-right">Cache Write /MTok</TableHead>
|
|
157
|
+
<TableHead className="text-right">Cache Read /MTok</TableHead>
|
|
158
|
+
</TableRow>
|
|
159
|
+
</TableHeader>
|
|
160
|
+
<TableBody>
|
|
161
|
+
{Object.entries(PRICING).map(([model, p]) => (
|
|
162
|
+
<TableRow key={model}>
|
|
163
|
+
<TableCell className="font-mono text-sm">{model}</TableCell>
|
|
164
|
+
<TableCell className="text-right font-mono text-blue-700 dark:text-[#60a5fa]">${(p.input * 1_000_000).toFixed(2)}</TableCell>
|
|
165
|
+
<TableCell className="text-right font-mono text-[#d97706]">${(p.output * 1_000_000).toFixed(2)}</TableCell>
|
|
166
|
+
<TableCell className="text-right font-mono text-[#a78bfa]">${(p.cacheWrite * 1_000_000).toFixed(2)}</TableCell>
|
|
167
|
+
<TableCell className="text-right font-mono text-[#34d399]">${(p.cacheRead * 1_000_000).toFixed(2)}</TableCell>
|
|
168
|
+
</TableRow>
|
|
169
|
+
))}
|
|
170
|
+
</TableBody>
|
|
171
|
+
</Table>
|
|
172
|
+
</CardContent>
|
|
173
|
+
</Card>
|
|
174
|
+
</>
|
|
175
|
+
)}
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
)
|
|
179
|
+
}
|