@tokcalc/mcp-server 0.1.3 → 0.2.0-alpha.1
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 +86 -406
- package/dist/http.js +23504 -0
- package/dist/index.js +21290 -0
- package/package.json +36 -92
- package/.zscripts/build.sh +0 -175
- package/.zscripts/database-runtime-build.sh +0 -33
- package/.zscripts/dev.pid +0 -1
- package/.zscripts/dev.sh +0 -154
- package/.zscripts/mini-services-build.sh +0 -78
- package/.zscripts/mini-services-install.sh +0 -65
- package/.zscripts/mini-services-start.sh +0 -123
- package/.zscripts/python-runtime-build.sh +0 -120
- package/.zscripts/start.sh +0 -145
- package/CAPACITY_STUDY.md +0 -283
- package/CODE_OF_CONDUCT.md +0 -55
- package/CONTRIBUTING.md +0 -177
- package/Caddyfile +0 -23
- package/LICENSE +0 -204
- package/bun.lock +0 -1965
- package/components.json +0 -21
- package/db/custom.db +0 -0
- package/download/README.md +0 -1
- package/download/tokcalc-dark-calculator.png +0 -0
- package/download/tokcalc-dark-default.png +0 -0
- package/download/tokcalc-demo.webm +0 -0
- package/download/tokcalc-github-link.png +0 -0
- package/download/tokcalc-hydration-fixed.png +0 -0
- package/download/tokcalc-issue-resolved.png +0 -0
- package/download/tokcalc-light-mode.png +0 -0
- package/download/tokcalc-light-reference.png +0 -0
- package/download/tokcalc-long-context-qwen.png +0 -0
- package/download/tokcalc-long-context.png +0 -0
- package/download/tokcalc-og-image-preview.png +0 -0
- package/download/tokcalc-phase2-3.png +0 -0
- package/download/tokcalc-plain-english.png +0 -0
- package/download/tokcalc-preview.png +0 -0
- package/download/tokcalc-share-bvb.png +0 -0
- package/download/tokcalc-share-feature.png +0 -0
- package/download/tokcalc-tab-build-vs-buy.png +0 -0
- package/download/tokcalc-tab-calculator.png +0 -0
- package/download/tokcalc-tab-reference.png +0 -0
- package/eslint.config.mjs +0 -50
- package/examples/websocket/frontend.tsx +0 -196
- package/examples/websocket/server.ts +0 -138
- package/mini-services/.gitkeep +0 -0
- package/mini-services/mcp-server/README.md +0 -86
- package/mini-services/mcp-server/bun.lock +0 -202
- package/mini-services/mcp-server/index.ts +0 -504
- package/mini-services/mcp-server/package.json +0 -40
- package/next.config.ts +0 -12
- package/postcss.config.mjs +0 -5
- package/prisma/schema.prisma +0 -32
- package/public/google6f58ca6be85fa903.html +0 -1
- package/public/logo.svg +0 -29
- package/public/manifest.json +0 -51
- package/public/og-icon-256.png +0 -0
- package/public/og.png +0 -0
- package/public/robots.txt +0 -25
- package/public/sitemap.xml +0 -23
- package/public/tokcalc-demo.gif +0 -0
- package/scripts/og-template.html +0 -120
- package/scripts/render-og.mjs +0 -43
- package/server.json +0 -21
- package/src/app/api/pricing/aws/route.ts +0 -186
- package/src/app/api/pricing/azure/route.ts +0 -168
- package/src/app/api/pricing/gcp/route.ts +0 -230
- package/src/app/api/pricing/vast-ai/route.ts +0 -164
- package/src/app/api/route.ts +0 -5
- package/src/app/compare/h100-vs-h200/layout.tsx +0 -30
- package/src/app/compare/h100-vs-h200/page.tsx +0 -328
- package/src/app/globals.css +0 -122
- package/src/app/layout.tsx +0 -276
- package/src/app/page.tsx +0 -2670
- package/src/components/azure-live-pricing.tsx +0 -185
- package/src/components/benchmark-import.tsx +0 -340
- package/src/components/confidence-badge.tsx +0 -116
- package/src/components/live-pricing-comparison.tsx +0 -241
- package/src/components/theme-provider.tsx +0 -11
- package/src/components/theme-toggle.tsx +0 -55
- package/src/components/ui/accordion.tsx +0 -66
- package/src/components/ui/alert-dialog.tsx +0 -157
- package/src/components/ui/alert.tsx +0 -66
- package/src/components/ui/aspect-ratio.tsx +0 -11
- package/src/components/ui/avatar.tsx +0 -53
- package/src/components/ui/badge.tsx +0 -46
- package/src/components/ui/breadcrumb.tsx +0 -109
- package/src/components/ui/button.tsx +0 -59
- package/src/components/ui/calendar.tsx +0 -213
- package/src/components/ui/card.tsx +0 -92
- package/src/components/ui/carousel.tsx +0 -241
- package/src/components/ui/chart.tsx +0 -353
- package/src/components/ui/checkbox.tsx +0 -32
- package/src/components/ui/collapsible.tsx +0 -33
- package/src/components/ui/command.tsx +0 -184
- package/src/components/ui/context-menu.tsx +0 -252
- package/src/components/ui/dialog.tsx +0 -143
- package/src/components/ui/drawer.tsx +0 -135
- package/src/components/ui/dropdown-menu.tsx +0 -257
- package/src/components/ui/form.tsx +0 -167
- package/src/components/ui/hover-card.tsx +0 -44
- package/src/components/ui/input-otp.tsx +0 -77
- package/src/components/ui/input.tsx +0 -21
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/menubar.tsx +0 -276
- package/src/components/ui/navigation-menu.tsx +0 -168
- package/src/components/ui/pagination.tsx +0 -127
- package/src/components/ui/popover.tsx +0 -48
- package/src/components/ui/progress.tsx +0 -31
- package/src/components/ui/radio-group.tsx +0 -45
- package/src/components/ui/resizable.tsx +0 -56
- package/src/components/ui/scroll-area.tsx +0 -58
- package/src/components/ui/select.tsx +0 -185
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/sidebar.tsx +0 -726
- package/src/components/ui/skeleton.tsx +0 -13
- package/src/components/ui/slider.tsx +0 -63
- package/src/components/ui/sonner.tsx +0 -25
- package/src/components/ui/switch.tsx +0 -31
- package/src/components/ui/table.tsx +0 -116
- package/src/components/ui/tabs.tsx +0 -66
- package/src/components/ui/textarea.tsx +0 -18
- package/src/components/ui/toast.tsx +0 -129
- package/src/components/ui/toaster.tsx +0 -35
- package/src/components/ui/toggle-group.tsx +0 -73
- package/src/components/ui/toggle.tsx +0 -47
- package/src/components/ui/tooltip.tsx +0 -61
- package/src/components/vast-ai-live-pricing.tsx +0 -176
- package/src/hooks/use-mobile.ts +0 -19
- package/src/hooks/use-toast.ts +0 -194
- package/src/lib/benchmark-parser-sglang.ts +0 -150
- package/src/lib/benchmark-parser-tokcalc.ts +0 -247
- package/src/lib/benchmark-parser-trtllm.ts +0 -152
- package/src/lib/benchmark-parser-vllm.ts +0 -198
- package/src/lib/benchmark-schema.ts +0 -263
- package/src/lib/db.ts +0 -13
- package/src/lib/engine-presets.ts +0 -183
- package/src/lib/price-schema.ts +0 -141
- package/src/lib/token-calc.ts +0 -808
- package/src/lib/track.ts +0 -31
- package/src/lib/url-state.ts +0 -256
- package/src/lib/utils.ts +0 -6
- package/tailwind.config.ts +0 -64
- package/tests/database-runtime-build.sh +0 -75
- package/tests/python-runtime-build.sh +0 -64
- package/tests/python-runtime-container.sh +0 -31
- package/tool-results/bash_1789888171144_2c5381860539.txt +0 -161
- package/tool-results/bash_1789888175925_49c53ba3c61b.txt +0 -191
- package/tool-results/bash_1789888181202_49c53ba3c61b.txt +0 -191
- package/tool-results/bash_1789888195219_4a86a5c91411.txt +0 -200
- package/tool-results/bash_1789888203128_6cca13c71b47.txt +0 -199
- package/tool-results/bash_1789929256963_2a52aff0d0a8.txt +0 -160
- package/tool-results/read_1789888151021_69f58eec6a5b.txt +0 -653
- package/tool-results/read_1789888153837_1d3a8bfc2a94.txt +0 -653
- package/tool-results/read_1789888163087_ccc406d47505.txt +0 -122
- package/tool-results/read_1789888167347_67d1d7c9830a.txt +0 -122
- package/tool-results/read_1789929252529_d90e8f383a25.txt +0 -285
- package/tsconfig.json +0 -42
- package/upload/Pasted Content_1789887800864.txt +0 -652
- package/upload/Pasted Content_1789887909561.txt +0 -652
- package/upload/Pasted Content_1789887918428.txt +0 -652
- package/upload/Pasted Content_1789887959420.txt +0 -652
- package/upload/Pasted Content_1789888020485.txt +0 -652
- package/upload/Pasted Content_1789888058079.txt +0 -652
- package/upload/Pasted Content_1789888885033.txt +0 -686
- package/upload/Pasted Content_1789928912741.txt +0 -285
- package/upload/Pasted Content_1789928938402.txt +0 -285
- package/upload/Pasted Content_1789929160389.txt +0 -285
- package/upload/Pasted Content_1789929176660.txt +0 -285
- package/upload/issue_vision.json +0 -28
- package/upload/pasted_image_1789883175209.png +0 -0
- package/upload/pasted_image_1789899056690.png +0 -0
- package/upload/pasted_image_1789900371483.png +0 -0
- package/upload/pasted_image_1789900472823.png +0 -0
- package/upload/pasted_image_1789900490374.png +0 -0
- package/upload/pasted_image_1789900585552.png +0 -0
- package/upload/pasted_image_1789900606519.png +0 -0
- package/upload/pasted_image_1789901598705.png +0 -0
- package/upload/pasted_image_1789901613545.png +0 -0
- package/upload/pasted_image_1789978382674.png +0 -0
- package/upload/pasted_image_1789978392749.png +0 -0
- package/upload/pasted_image_1789978474879.png +0 -0
- package/upload/pasted_image_1789978523652.png +0 -0
- package/upload/pasted_image_1789984219089.png +0 -0
- package/upload/pasted_image_1789984491896.png +0 -0
- package/upload/pasted_image_1789985017950.png +0 -0
- package/upload/pasted_image_1789985036765.png +0 -0
- package/upload/pasted_image_1789985049848.png +0 -0
- package/upload/pasted_image_1790002427833.png +0 -0
- package/upload/pasted_image_1790002659944.png +0 -0
- package/upload/pasted_image_1790037038476.png +0 -0
- package/upload/screenshot_analysis.json +0 -28
- package/upload/vision_output.json +0 -28
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
4
|
-
import {
|
|
5
|
-
Card,
|
|
6
|
-
CardContent,
|
|
7
|
-
CardDescription,
|
|
8
|
-
CardHeader,
|
|
9
|
-
CardTitle,
|
|
10
|
-
} from "@/components/ui/card";
|
|
11
|
-
import { Button } from "@/components/ui/button";
|
|
12
|
-
import { Badge } from "@/components/ui/badge";
|
|
13
|
-
import { RefreshCw, Zap, AlertTriangle, TrendingDown } from "lucide-react";
|
|
14
|
-
import { track } from "@/lib/track";
|
|
15
|
-
|
|
16
|
-
interface VastAiOffer {
|
|
17
|
-
gpu: string;
|
|
18
|
-
gpuCount: number;
|
|
19
|
-
vramGb: number;
|
|
20
|
-
pricePerGpuHour: number;
|
|
21
|
-
totalPriceHour: number;
|
|
22
|
-
reliability: number;
|
|
23
|
-
region: string;
|
|
24
|
-
bundleId: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface VastAiApiResponse {
|
|
28
|
-
source: string;
|
|
29
|
-
cached: boolean;
|
|
30
|
-
retrievedAt?: string;
|
|
31
|
-
count?: number;
|
|
32
|
-
offers?: VastAiOffer[];
|
|
33
|
-
note?: string;
|
|
34
|
-
error?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function VastAiLivePricing() {
|
|
38
|
-
const [data, setData] = useState<VastAiApiResponse | null>(null);
|
|
39
|
-
const [loading, setLoading] = useState(false);
|
|
40
|
-
const [error, setError] = useState<string | null>(null);
|
|
41
|
-
|
|
42
|
-
const fetchPrices = async () => {
|
|
43
|
-
setLoading(true);
|
|
44
|
-
setError(null);
|
|
45
|
-
try {
|
|
46
|
-
const res = await fetch("/api/pricing/vast-ai");
|
|
47
|
-
if (!res.ok) {
|
|
48
|
-
const errData = await res.json().catch(() => ({}));
|
|
49
|
-
throw new Error(errData.error || `HTTP ${res.status}`);
|
|
50
|
-
}
|
|
51
|
-
const json = (await res.json()) as VastAiApiResponse;
|
|
52
|
-
setData(json);
|
|
53
|
-
track("fetched_live_pricing", { provider: "vast_ai", count: json.count });
|
|
54
|
-
} catch (e) {
|
|
55
|
-
setError(e instanceof Error ? e.message : "Failed to fetch Vast.ai pricing");
|
|
56
|
-
} finally {
|
|
57
|
-
setLoading(false);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
fetchPrices();
|
|
63
|
-
}, []);
|
|
64
|
-
|
|
65
|
-
const asOfLabel = (() => {
|
|
66
|
-
if (!data?.retrievedAt) return null;
|
|
67
|
-
const ageMs = Date.now() - new Date(data.retrievedAt).getTime();
|
|
68
|
-
if (ageMs < 60000) return "just now";
|
|
69
|
-
if (ageMs < 3600000) return `${Math.floor(ageMs / 60000)}m ago`;
|
|
70
|
-
return `${Math.floor(ageMs / 3600000)}h ago`;
|
|
71
|
-
})();
|
|
72
|
-
|
|
73
|
-
const offers = data?.offers || [];
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<Card className="border-amber-500/30 shadow-sm">
|
|
77
|
-
<CardHeader className="pb-3">
|
|
78
|
-
<div className="flex items-center justify-between">
|
|
79
|
-
<div>
|
|
80
|
-
<CardTitle className="text-sm flex items-center gap-2">
|
|
81
|
-
<TrendingDown className="size-4 text-amber-500" />
|
|
82
|
-
Vast.ai marketplace — spot prices
|
|
83
|
-
<Badge variant="outline" className="text-[9px] gap-1 ml-1">
|
|
84
|
-
<span className="size-1.5 rounded-full bg-amber-500 animate-pulse" />
|
|
85
|
-
LIVE
|
|
86
|
-
</Badge>
|
|
87
|
-
</CardTitle>
|
|
88
|
-
<CardDescription className="text-xs mt-1">
|
|
89
|
-
Cheapest available GPU offers on Vast.ai marketplace right now.
|
|
90
|
-
{asOfLabel && ` Last updated ${asOfLabel}.`}
|
|
91
|
-
{" "}Spot prices change rapidly — individual offers may be gone by the time you rent.
|
|
92
|
-
</CardDescription>
|
|
93
|
-
</div>
|
|
94
|
-
<Button
|
|
95
|
-
variant="ghost"
|
|
96
|
-
size="sm"
|
|
97
|
-
onClick={fetchPrices}
|
|
98
|
-
disabled={loading}
|
|
99
|
-
className="gap-1.5 h-8"
|
|
100
|
-
>
|
|
101
|
-
<RefreshCw className={`size-3.5 ${loading ? "animate-spin" : ""}`} />
|
|
102
|
-
<span className="text-xs">Refresh</span>
|
|
103
|
-
</Button>
|
|
104
|
-
</div>
|
|
105
|
-
</CardHeader>
|
|
106
|
-
<CardContent>
|
|
107
|
-
{loading && !data && (
|
|
108
|
-
<div className="flex items-center justify-center py-8 text-xs text-muted-foreground">
|
|
109
|
-
<RefreshCw className="size-4 animate-spin mr-2" />
|
|
110
|
-
Fetching Vast.ai marketplace offers...
|
|
111
|
-
</div>
|
|
112
|
-
)}
|
|
113
|
-
|
|
114
|
-
{error && (
|
|
115
|
-
<div className="flex items-start gap-2 p-3 rounded-md border border-amber-500/30 bg-amber-500/5 text-xs">
|
|
116
|
-
<AlertTriangle className="size-3.5 text-amber-500 shrink-0 mt-0.5" />
|
|
117
|
-
<div>
|
|
118
|
-
<p className="font-medium text-amber-600 dark:text-amber-400">
|
|
119
|
-
Couldn't fetch marketplace prices
|
|
120
|
-
</p>
|
|
121
|
-
<p className="text-muted-foreground mt-0.5">{error}</p>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
)}
|
|
125
|
-
|
|
126
|
-
{offers.length > 0 && (
|
|
127
|
-
<div className="overflow-x-auto">
|
|
128
|
-
<table className="w-full text-xs">
|
|
129
|
-
<thead className="bg-muted/40 text-[10px] uppercase">
|
|
130
|
-
<tr>
|
|
131
|
-
<th className="text-left px-3 py-1.5">GPU</th>
|
|
132
|
-
<th className="text-right px-3 py-1.5">$/GPU-hr</th>
|
|
133
|
-
<th className="text-right px-3 py-1.5">GPU count</th>
|
|
134
|
-
<th className="text-right px-3 py-1.5">Total $/hr</th>
|
|
135
|
-
<th className="text-right px-3 py-1.5">VRAM</th>
|
|
136
|
-
<th className="text-right px-3 py-1.5">Reliability</th>
|
|
137
|
-
<th className="text-left px-3 py-1.5">Region</th>
|
|
138
|
-
</tr>
|
|
139
|
-
</thead>
|
|
140
|
-
<tbody>
|
|
141
|
-
{offers.slice(0, 15).map((o, i) => (
|
|
142
|
-
<tr key={i} className="border-t border-border/40 hover:bg-muted/20">
|
|
143
|
-
<td className="px-3 py-1.5 font-medium">{o.gpu}</td>
|
|
144
|
-
<td className="px-3 py-1.5 text-right font-mono text-amber-500 font-semibold">
|
|
145
|
-
${o.pricePerGpuHour}/hr
|
|
146
|
-
</td>
|
|
147
|
-
<td className="px-3 py-1.5 text-right font-mono">{o.gpuCount}×</td>
|
|
148
|
-
<td className="px-3 py-1.5 text-right font-mono text-muted-foreground">${o.totalPriceHour}</td>
|
|
149
|
-
<td className="px-3 py-1.5 text-right font-mono text-muted-foreground">{o.vramGb}GB</td>
|
|
150
|
-
<td className="px-3 py-1.5 text-right font-mono">
|
|
151
|
-
<span className={o.reliability > 0.9 ? "text-emerald-500" : o.reliability > 0.7 ? "text-amber-500" : "text-red-500"}>
|
|
152
|
-
{(o.reliability * 100).toFixed(0)}%
|
|
153
|
-
</span>
|
|
154
|
-
</td>
|
|
155
|
-
<td className="px-3 py-1.5 text-muted-foreground text-[10px]">{o.region || "—"}</td>
|
|
156
|
-
</tr>
|
|
157
|
-
))}
|
|
158
|
-
</tbody>
|
|
159
|
-
</table>
|
|
160
|
-
<p className="text-[10px] text-muted-foreground mt-2 leading-relaxed">
|
|
161
|
-
Spot-market prices from Vast.ai marketplace.
|
|
162
|
-
Reliability score (0-100%) indicates host trustworthiness.
|
|
163
|
-
Total $/hr is the full bundle price (all GPUs + CPU + RAM).
|
|
164
|
-
Source: <a
|
|
165
|
-
href="https://vast.ai/pricing"
|
|
166
|
-
target="_blank"
|
|
167
|
-
rel="noopener noreferrer"
|
|
168
|
-
className="text-amber-500 hover:underline"
|
|
169
|
-
>Vast.ai marketplace API ↗</a>
|
|
170
|
-
</p>
|
|
171
|
-
</div>
|
|
172
|
-
)}
|
|
173
|
-
</CardContent>
|
|
174
|
-
</Card>
|
|
175
|
-
);
|
|
176
|
-
}
|
package/src/hooks/use-mobile.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
const MOBILE_BREAKPOINT = 768
|
|
4
|
-
|
|
5
|
-
export function useIsMobile() {
|
|
6
|
-
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
|
7
|
-
|
|
8
|
-
React.useEffect(() => {
|
|
9
|
-
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
-
const onChange = () => {
|
|
11
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
-
}
|
|
13
|
-
mql.addEventListener("change", onChange)
|
|
14
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
-
return () => mql.removeEventListener("change", onChange)
|
|
16
|
-
}, [])
|
|
17
|
-
|
|
18
|
-
return !!isMobile
|
|
19
|
-
}
|
package/src/hooks/use-toast.ts
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
// Inspired by react-hot-toast library
|
|
4
|
-
import * as React from "react"
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
ToastActionElement,
|
|
8
|
-
ToastProps,
|
|
9
|
-
} from "@/components/ui/toast"
|
|
10
|
-
|
|
11
|
-
const TOAST_LIMIT = 1
|
|
12
|
-
const TOAST_REMOVE_DELAY = 1000000
|
|
13
|
-
|
|
14
|
-
type ToasterToast = ToastProps & {
|
|
15
|
-
id: string
|
|
16
|
-
title?: React.ReactNode
|
|
17
|
-
description?: React.ReactNode
|
|
18
|
-
action?: ToastActionElement
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const actionTypes = {
|
|
22
|
-
ADD_TOAST: "ADD_TOAST",
|
|
23
|
-
UPDATE_TOAST: "UPDATE_TOAST",
|
|
24
|
-
DISMISS_TOAST: "DISMISS_TOAST",
|
|
25
|
-
REMOVE_TOAST: "REMOVE_TOAST",
|
|
26
|
-
} as const
|
|
27
|
-
|
|
28
|
-
let count = 0
|
|
29
|
-
|
|
30
|
-
function genId() {
|
|
31
|
-
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
|
32
|
-
return count.toString()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
type ActionType = typeof actionTypes
|
|
36
|
-
|
|
37
|
-
type Action =
|
|
38
|
-
| {
|
|
39
|
-
type: ActionType["ADD_TOAST"]
|
|
40
|
-
toast: ToasterToast
|
|
41
|
-
}
|
|
42
|
-
| {
|
|
43
|
-
type: ActionType["UPDATE_TOAST"]
|
|
44
|
-
toast: Partial<ToasterToast>
|
|
45
|
-
}
|
|
46
|
-
| {
|
|
47
|
-
type: ActionType["DISMISS_TOAST"]
|
|
48
|
-
toastId?: ToasterToast["id"]
|
|
49
|
-
}
|
|
50
|
-
| {
|
|
51
|
-
type: ActionType["REMOVE_TOAST"]
|
|
52
|
-
toastId?: ToasterToast["id"]
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
interface State {
|
|
56
|
-
toasts: ToasterToast[]
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
|
60
|
-
|
|
61
|
-
const addToRemoveQueue = (toastId: string) => {
|
|
62
|
-
if (toastTimeouts.has(toastId)) {
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const timeout = setTimeout(() => {
|
|
67
|
-
toastTimeouts.delete(toastId)
|
|
68
|
-
dispatch({
|
|
69
|
-
type: "REMOVE_TOAST",
|
|
70
|
-
toastId: toastId,
|
|
71
|
-
})
|
|
72
|
-
}, TOAST_REMOVE_DELAY)
|
|
73
|
-
|
|
74
|
-
toastTimeouts.set(toastId, timeout)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export const reducer = (state: State, action: Action): State => {
|
|
78
|
-
switch (action.type) {
|
|
79
|
-
case "ADD_TOAST":
|
|
80
|
-
return {
|
|
81
|
-
...state,
|
|
82
|
-
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
case "UPDATE_TOAST":
|
|
86
|
-
return {
|
|
87
|
-
...state,
|
|
88
|
-
toasts: state.toasts.map((t) =>
|
|
89
|
-
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
|
90
|
-
),
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
case "DISMISS_TOAST": {
|
|
94
|
-
const { toastId } = action
|
|
95
|
-
|
|
96
|
-
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
|
97
|
-
// but I'll keep it here for simplicity
|
|
98
|
-
if (toastId) {
|
|
99
|
-
addToRemoveQueue(toastId)
|
|
100
|
-
} else {
|
|
101
|
-
state.toasts.forEach((toast) => {
|
|
102
|
-
addToRemoveQueue(toast.id)
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
...state,
|
|
108
|
-
toasts: state.toasts.map((t) =>
|
|
109
|
-
t.id === toastId || toastId === undefined
|
|
110
|
-
? {
|
|
111
|
-
...t,
|
|
112
|
-
open: false,
|
|
113
|
-
}
|
|
114
|
-
: t
|
|
115
|
-
),
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
case "REMOVE_TOAST":
|
|
119
|
-
if (action.toastId === undefined) {
|
|
120
|
-
return {
|
|
121
|
-
...state,
|
|
122
|
-
toasts: [],
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return {
|
|
126
|
-
...state,
|
|
127
|
-
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const listeners: Array<(state: State) => void> = []
|
|
133
|
-
|
|
134
|
-
let memoryState: State = { toasts: [] }
|
|
135
|
-
|
|
136
|
-
function dispatch(action: Action) {
|
|
137
|
-
memoryState = reducer(memoryState, action)
|
|
138
|
-
listeners.forEach((listener) => {
|
|
139
|
-
listener(memoryState)
|
|
140
|
-
})
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
type Toast = Omit<ToasterToast, "id">
|
|
144
|
-
|
|
145
|
-
function toast({ ...props }: Toast) {
|
|
146
|
-
const id = genId()
|
|
147
|
-
|
|
148
|
-
const update = (props: ToasterToast) =>
|
|
149
|
-
dispatch({
|
|
150
|
-
type: "UPDATE_TOAST",
|
|
151
|
-
toast: { ...props, id },
|
|
152
|
-
})
|
|
153
|
-
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
|
|
154
|
-
|
|
155
|
-
dispatch({
|
|
156
|
-
type: "ADD_TOAST",
|
|
157
|
-
toast: {
|
|
158
|
-
...props,
|
|
159
|
-
id,
|
|
160
|
-
open: true,
|
|
161
|
-
onOpenChange: (open) => {
|
|
162
|
-
if (!open) dismiss()
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
id: id,
|
|
169
|
-
dismiss,
|
|
170
|
-
update,
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function useToast() {
|
|
175
|
-
const [state, setState] = React.useState<State>(memoryState)
|
|
176
|
-
|
|
177
|
-
React.useEffect(() => {
|
|
178
|
-
listeners.push(setState)
|
|
179
|
-
return () => {
|
|
180
|
-
const index = listeners.indexOf(setState)
|
|
181
|
-
if (index > -1) {
|
|
182
|
-
listeners.splice(index, 1)
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}, [state])
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
...state,
|
|
189
|
-
toast,
|
|
190
|
-
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export { useToast, toast }
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SGLang benchmark parser.
|
|
3
|
-
*
|
|
4
|
-
* SGLang's benchmark scripts produce JSON/log output with fields like:
|
|
5
|
-
* - total_throughput or output_throughput (tokens/s)
|
|
6
|
-
* - mean_ttft or ttft_avg (ms)
|
|
7
|
-
* - mean_itl or itl_avg (ms)
|
|
8
|
-
* - total_input_tokens, total_output_tokens
|
|
9
|
-
* - num_requests
|
|
10
|
-
* - concurrency
|
|
11
|
-
* - model_id
|
|
12
|
-
*
|
|
13
|
-
* SGLang output format varies by benchmark script version. This parser
|
|
14
|
-
* handles common aliases across SGLang v0.3.x → v0.5.x.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import type {
|
|
18
|
-
BenchmarkParser,
|
|
19
|
-
BenchmarkRecord,
|
|
20
|
-
ConfidenceTier,
|
|
21
|
-
} from "./benchmark-schema";
|
|
22
|
-
import { generateBenchmarkId } from "./benchmark-schema";
|
|
23
|
-
|
|
24
|
-
export const sglangParser: BenchmarkParser = {
|
|
25
|
-
name: "sglang-json",
|
|
26
|
-
version: "v1",
|
|
27
|
-
formatLabel: "SGLang benchmark JSON",
|
|
28
|
-
|
|
29
|
-
detect(rawText: string): boolean {
|
|
30
|
-
try {
|
|
31
|
-
const data = JSON.parse(rawText);
|
|
32
|
-
return !!(
|
|
33
|
-
data &&
|
|
34
|
-
(data.total_throughput ||
|
|
35
|
-
data.output_throughput ||
|
|
36
|
-
data.mean_ttft ||
|
|
37
|
-
data.ttft_avg ||
|
|
38
|
-
data.mean_itl ||
|
|
39
|
-
data.itl_avg ||
|
|
40
|
-
data.num_requests ||
|
|
41
|
-
data.total_input_tokens)
|
|
42
|
-
);
|
|
43
|
-
} catch {
|
|
44
|
-
// Also check for SGLang log format (not JSON)
|
|
45
|
-
return rawText.includes("sglang") && rawText.includes("throughput");
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
parse(rawText: string): BenchmarkRecord | null {
|
|
50
|
-
try {
|
|
51
|
-
// Try JSON first
|
|
52
|
-
const data = JSON.parse(rawText);
|
|
53
|
-
if (!data) return null;
|
|
54
|
-
|
|
55
|
-
const modelId = data.model_id || data.model || "unknown";
|
|
56
|
-
const modelDisplayName = modelId.split("/").pop() || modelId;
|
|
57
|
-
|
|
58
|
-
const outputThroughput =
|
|
59
|
-
data.output_throughput ?? data.total_throughput ?? data.throughput ?? 0;
|
|
60
|
-
const meanTtft = data.mean_ttft ?? data.ttft_avg ?? data.ttft_mean;
|
|
61
|
-
const p99Ttft = data.p99_ttft ?? data.ttft_p99;
|
|
62
|
-
const meanItl = data.mean_itl ?? data.itl_avg ?? data.itl_mean;
|
|
63
|
-
const p99Itl = data.p99_itl ?? data.itl_p99;
|
|
64
|
-
|
|
65
|
-
const numRequests = data.num_requests ?? data.completed ?? 0;
|
|
66
|
-
const inputLen = Number(data.input_len ?? data.mean_input_len ?? 0);
|
|
67
|
-
const outputLen = Number(data.output_len ?? data.mean_output_len ?? 0);
|
|
68
|
-
const concurrency = data.concurrency ?? data.num_procs ?? 1;
|
|
69
|
-
|
|
70
|
-
const confidenceTier: ConfidenceTier =
|
|
71
|
-
modelId !== "unknown" && outputThroughput > 0 && numRequests > 0
|
|
72
|
-
? "community_reproducible"
|
|
73
|
-
: "community_unverified";
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
benchmark_id: generateBenchmarkId(),
|
|
77
|
-
source_name: "sglang",
|
|
78
|
-
source_url: data.source_url || "https://github.com/sgl-project/sglang",
|
|
79
|
-
source_license: "Apache-2.0",
|
|
80
|
-
ingestion_method: "uploaded_raw_artifact",
|
|
81
|
-
retrieved_at: new Date().toISOString(),
|
|
82
|
-
parser_name: "sglang-json",
|
|
83
|
-
parser_version: "v1",
|
|
84
|
-
confidence_tier: confidenceTier,
|
|
85
|
-
verification_status: "schema_valid",
|
|
86
|
-
|
|
87
|
-
model_id: modelId,
|
|
88
|
-
model_display_name: modelDisplayName,
|
|
89
|
-
model_revision: data.model_revision || "unspecified",
|
|
90
|
-
architecture: data.architecture,
|
|
91
|
-
parameter_count_b: data.parameter_count_b,
|
|
92
|
-
context_window_tokens: data.max_model_len,
|
|
93
|
-
quantization_format: data.quantization || data.dtype || "unspecified",
|
|
94
|
-
weight_dtype: data.dtype,
|
|
95
|
-
kv_cache_dtype: data.kv_cache_dtype || "float16",
|
|
96
|
-
|
|
97
|
-
accelerator_vendor: "NVIDIA",
|
|
98
|
-
accelerator_model: data.gpu_model || "unspecified",
|
|
99
|
-
gpu_count: data.tensor_parallel_size || data.gpu_count || 1,
|
|
100
|
-
memory_per_gpu_gb: data.gpu_memory || 0,
|
|
101
|
-
driver_version: data.driver_version,
|
|
102
|
-
cuda_rocm_metal_version: data.cuda_version,
|
|
103
|
-
|
|
104
|
-
serving_engine: "SGLang",
|
|
105
|
-
engine_version: data.sglang_version || "unspecified",
|
|
106
|
-
engine_command: data.command,
|
|
107
|
-
engine_config: {
|
|
108
|
-
tensor_parallel_size: data.tensor_parallel_size,
|
|
109
|
-
max_model_len: data.max_model_len,
|
|
110
|
-
max_num_seqs: data.max_num_seqs,
|
|
111
|
-
prefix_cache_enabled: data.enable_prefix_caching ?? data.radix_cache,
|
|
112
|
-
chunked_prefill_enabled: data.enable_chunked_prefill,
|
|
113
|
-
attention_backend: data.attention_backend,
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
workload_type: data.workload_type || "online_poisson",
|
|
117
|
-
dataset_name: data.dataset_name || data.dataset,
|
|
118
|
-
request_count: numRequests,
|
|
119
|
-
concurrency,
|
|
120
|
-
input_tokens_mean: inputLen,
|
|
121
|
-
output_tokens_mean: outputLen,
|
|
122
|
-
streaming_enabled: data.streaming ?? true,
|
|
123
|
-
|
|
124
|
-
successful_requests: numRequests,
|
|
125
|
-
failed_requests: data.failed ?? 0,
|
|
126
|
-
output_token_throughput_tps: outputThroughput,
|
|
127
|
-
total_token_throughput_tps: data.total_token_throughput,
|
|
128
|
-
request_throughput_rps: data.request_throughput,
|
|
129
|
-
ttft_mean_ms: meanTtft,
|
|
130
|
-
ttft_p50_ms: data.p50_ttft ?? data.median_ttft,
|
|
131
|
-
ttft_p95_ms: data.p95_ttft,
|
|
132
|
-
ttft_p99_ms: p99Ttft,
|
|
133
|
-
itl_mean_ms: meanItl,
|
|
134
|
-
itl_p50_ms: data.p50_itl ?? data.median_itl,
|
|
135
|
-
itl_p95_ms: data.p95_itl,
|
|
136
|
-
itl_p99_ms: p99Itl,
|
|
137
|
-
|
|
138
|
-
gpu_memory_peak_gb: data.peak_gpu_memory,
|
|
139
|
-
|
|
140
|
-
metric_definition_version: "sglang-v1",
|
|
141
|
-
citation_text: `SGLang benchmark: ${outputThroughput.toFixed(2)} tok/s, TTFT mean ${meanTtft?.toFixed(1) || "?"} ms, ${numRequests} requests`,
|
|
142
|
-
attribution_required: true,
|
|
143
|
-
commercial_redistribution_allowed: "yes",
|
|
144
|
-
};
|
|
145
|
-
} catch {
|
|
146
|
-
// Not JSON — try log format (future: parse SGLang text logs)
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
};
|