Caleb Fahlgren
commited on
Commit
•
32e1a9b
1
Parent(s):
eb17fc6
add finetune growth chart
Browse files- README.md +0 -1
- app/page.tsx +133 -95
- components/{area-chart.tsx → area-chart-stacked.tsx} +61 -55
- components/pie-chart.tsx +30 -24
- components/simple-area.tsx +97 -0
- lib/queries.ts +26 -0
README.md
CHANGED
@@ -4,4 +4,3 @@
|
|
4 |
- [Dataset](https://huggingface.co/datasets/cfahlgren1/hub-stats) updated on HuggingFace
|
5 |
|
6 |
![HF Stats](./stats.png)
|
7 |
-
|
|
|
4 |
- [Dataset](https://huggingface.co/datasets/cfahlgren1/hub-stats) updated on HuggingFace
|
5 |
|
6 |
![HF Stats](./stats.png)
|
|
app/page.tsx
CHANGED
@@ -2,77 +2,64 @@
|
|
2 |
|
3 |
import { useEffect, useState } from "react"
|
4 |
import * as duckdb from "@duckdb/duckdb-wasm"
|
|
|
5 |
|
6 |
-
import {
|
7 |
-
|
8 |
-
FETCH_CHART_DATA_QUERY,
|
9 |
-
FETCH_DATASET_LICENSE_DATA_QUERY,
|
10 |
-
FETCH_MODEL_LICENSE_DATA_QUERY,
|
11 |
-
FETCH_SPACE_SDK_DATA_QUERY,
|
12 |
-
} from "@/lib/queries"
|
13 |
-
import { AreaChartStacked, ChartDataPoint } from "@/components/area-chart"
|
14 |
import { CustomPieChart } from "@/components/pie-chart"
|
|
|
|
|
15 |
|
16 |
export default function IndexPage() {
|
17 |
const [conn, setConn] = useState<duckdb.AsyncDuckDBConnection | null>(null)
|
18 |
const [chartData, setChartData] = useState<ChartDataPoint[]>([])
|
19 |
-
const [modelLicenseData, setModelLicenseData] = useState<
|
20 |
-
|
21 |
-
|
22 |
-
const [
|
23 |
-
|
24 |
-
|
25 |
-
const [spaceSdkData, setSpaceSdkData] = useState<
|
26 |
-
Array<{ name: string; value: number; fill: string }>
|
27 |
-
>([])
|
28 |
|
29 |
useEffect(() => {
|
30 |
-
const initDB = async () => {
|
31 |
-
const CDN_BASE = `https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@next`
|
32 |
-
|
33 |
-
const JSDELIVR_BUNDLES = {
|
34 |
-
mvp: {
|
35 |
-
mainModule: `${CDN_BASE}/dist/duckdb-mvp.wasm`,
|
36 |
-
mainWorker: `${CDN_BASE}/dist/duckdb-browser-mvp.worker.js`,
|
37 |
-
},
|
38 |
-
eh: {
|
39 |
-
mainModule: `${CDN_BASE}/dist/duckdb-eh.wasm`,
|
40 |
-
mainWorker: `${CDN_BASE}/dist/duckdb-browser-eh.worker.js`,
|
41 |
-
},
|
42 |
-
}
|
43 |
-
|
44 |
-
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES)
|
45 |
-
const worker_url = URL.createObjectURL(
|
46 |
-
new Blob([`importScripts("${bundle.mainWorker}");`], {
|
47 |
-
type: "text/javascript",
|
48 |
-
})
|
49 |
-
)
|
50 |
-
|
51 |
-
const worker = new Worker(worker_url)
|
52 |
-
const logger = new duckdb.ConsoleLogger()
|
53 |
-
const db = new duckdb.AsyncDuckDB(logger, worker)
|
54 |
-
await db.instantiate(bundle.mainModule)
|
55 |
-
|
56 |
-
const connection = await db.connect()
|
57 |
-
|
58 |
-
await connection.query(CREATE_VIEWS_QUERY)
|
59 |
-
|
60 |
-
setConn(connection)
|
61 |
-
await fetchChartData(connection)
|
62 |
-
}
|
63 |
-
|
64 |
initDB()
|
|
|
|
|
65 |
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
}
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
const fetchChartData = async (connection: duckdb.AsyncDuckDBConnection) => {
|
75 |
-
// Use the imported query
|
76 |
const result = await connection.query(FETCH_CHART_DATA_QUERY)
|
77 |
|
78 |
const data: ChartDataPoint[] = result.toArray().map((row) => ({
|
@@ -116,47 +103,98 @@ export default function IndexPage() {
|
|
116 |
)
|
117 |
}
|
118 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
return (
|
120 |
<section className="container grid items-center gap-6 pb-8 pt-6 md:py-10">
|
121 |
-
<
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
</div>
|
126 |
<div className="flex flex-col gap-4 max-w-6xl mt-10 w-full mx-auto">
|
127 |
-
{chartData
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
</div>
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
</section>
|
161 |
)
|
162 |
}
|
|
|
2 |
|
3 |
import { useEffect, useState } from "react"
|
4 |
import * as duckdb from "@duckdb/duckdb-wasm"
|
5 |
+
import { Loader2 } from "lucide-react"
|
6 |
|
7 |
+
import { CREATE_VIEWS_QUERY, FETCH_CHART_DATA_QUERY, FETCH_DATASET_LICENSE_DATA_QUERY, FETCH_FINETUNE_MODEL_GROWTH_QUERY, FETCH_MODEL_LICENSE_DATA_QUERY, FETCH_SPACE_SDK_DATA_QUERY } from "@/lib/queries"
|
8 |
+
import { AreaChartStacked, ChartDataPoint } from "@/components/area-chart-stacked"
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
import { CustomPieChart } from "@/components/pie-chart"
|
10 |
+
import { SimpleArea } from "@/components/simple-area"
|
11 |
+
import { Button } from "@/components/ui/button"
|
12 |
|
13 |
export default function IndexPage() {
|
14 |
const [conn, setConn] = useState<duckdb.AsyncDuckDBConnection | null>(null)
|
15 |
const [chartData, setChartData] = useState<ChartDataPoint[]>([])
|
16 |
+
const [modelLicenseData, setModelLicenseData] = useState<Array<{ name: string; value: number; fill: string }>>([])
|
17 |
+
const [datasetLicenseData, setDatasetLicenseData] = useState<Array<{ name: string; value: number; fill: string }>>([])
|
18 |
+
const [spaceSdkData, setSpaceSdkData] = useState<Array<{ name: string; value: number; fill: string }>>([])
|
19 |
+
const [baseModel, setBaseModel] = useState("meta-llama/Meta-Llama-3-8B")
|
20 |
+
const [finetuneModelGrowthData, setFinetuneModelGrowthData] = useState<Array<{ date: Date; count: number }>>([])
|
21 |
+
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
|
|
|
22 |
|
23 |
useEffect(() => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
initDB()
|
25 |
+
return () => { if (conn) conn.close() }
|
26 |
+
}, [])
|
27 |
|
28 |
+
const initDB = async () => {
|
29 |
+
const CDN_BASE = `https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@next`
|
30 |
+
|
31 |
+
const JSDELIVR_BUNDLES = {
|
32 |
+
mvp: {
|
33 |
+
mainModule: `${CDN_BASE}/dist/duckdb-mvp.wasm`,
|
34 |
+
mainWorker: `${CDN_BASE}/dist/duckdb-browser-mvp.worker.js`,
|
35 |
+
},
|
36 |
+
eh: {
|
37 |
+
mainModule: `${CDN_BASE}/dist/duckdb-eh.wasm`,
|
38 |
+
mainWorker: `${CDN_BASE}/dist/duckdb-browser-eh.worker.js`,
|
39 |
+
},
|
40 |
}
|
41 |
+
|
42 |
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES)
|
43 |
+
const worker_url = URL.createObjectURL(
|
44 |
+
new Blob([`importScripts("${bundle.mainWorker}");`], {
|
45 |
+
type: "text/javascript",
|
46 |
+
})
|
47 |
+
)
|
48 |
+
|
49 |
+
const worker = new Worker(worker_url)
|
50 |
+
const logger = new duckdb.ConsoleLogger()
|
51 |
+
const db = new duckdb.AsyncDuckDB(logger, worker)
|
52 |
+
await db.instantiate(bundle.mainModule)
|
53 |
+
|
54 |
+
const connection = await db.connect()
|
55 |
+
|
56 |
+
await connection.query(CREATE_VIEWS_QUERY)
|
57 |
+
|
58 |
+
setConn(connection)
|
59 |
+
await fetchChartData(connection)
|
60 |
+
}
|
61 |
|
62 |
const fetchChartData = async (connection: duckdb.AsyncDuckDBConnection) => {
|
|
|
63 |
const result = await connection.query(FETCH_CHART_DATA_QUERY)
|
64 |
|
65 |
const data: ChartDataPoint[] = result.toArray().map((row) => ({
|
|
|
103 |
)
|
104 |
}
|
105 |
|
106 |
+
const handleBaseModelSubmit = async (e: React.FormEvent) => {
|
107 |
+
e.preventDefault()
|
108 |
+
if (!conn) {
|
109 |
+
console.warn("Database connection not established")
|
110 |
+
return
|
111 |
+
}
|
112 |
+
|
113 |
+
setIsLoading(true)
|
114 |
+
try {
|
115 |
+
console.log("Fetching finetune model growth data for", baseModel)
|
116 |
+
const result = await conn.query(FETCH_FINETUNE_MODEL_GROWTH_QUERY(baseModel))
|
117 |
+
console.log("Received Result")
|
118 |
+
const data = result.toArray().map((row: { date: Date; count: bigint }) => ({
|
119 |
+
date: new Date(row.date),
|
120 |
+
count: parseInt(row.count.toString())
|
121 |
+
}))
|
122 |
+
console.log("Setting finetune model growth data", data)
|
123 |
+
setFinetuneModelGrowthData(data)
|
124 |
+
} catch (error) {
|
125 |
+
console.error("Error executing query:", error)
|
126 |
+
} finally {
|
127 |
+
setIsLoading(false)
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
return (
|
132 |
<section className="container grid items-center gap-6 pb-8 pt-6 md:py-10">
|
133 |
+
<h1 className="text-3xl text-center font-extrabold leading-tight tracking-tighter md:text-4xl">
|
134 |
+
Hugging Face Hub Stats
|
135 |
+
</h1>
|
136 |
+
|
|
|
137 |
<div className="flex flex-col gap-4 max-w-6xl mt-10 w-full mx-auto">
|
138 |
+
<AreaChartStacked data={chartData} />
|
139 |
+
</div>
|
140 |
+
|
141 |
+
<div className="flex flex-wrap gap-8 max-w-6xl mt-10 w-full mx-auto">
|
142 |
+
<div className="flex-1 min-w-[300px]">
|
143 |
+
<CustomPieChart
|
144 |
+
title="Model Licenses"
|
145 |
+
data={modelLicenseData}
|
146 |
+
dataKey="value"
|
147 |
+
/>
|
148 |
+
</div>
|
149 |
+
<div className="flex-1 min-w-[300px]">
|
150 |
+
<CustomPieChart
|
151 |
+
title="Dataset Licenses"
|
152 |
+
data={datasetLicenseData}
|
153 |
+
dataKey="value"
|
154 |
+
/>
|
155 |
+
</div>
|
156 |
+
<div className="flex-1 min-w-[300px]">
|
157 |
+
<CustomPieChart
|
158 |
+
title="Space SDKs"
|
159 |
+
data={spaceSdkData}
|
160 |
+
dataKey="value"
|
161 |
+
/>
|
162 |
+
</div>
|
163 |
</div>
|
164 |
+
|
165 |
+
<div className="flex flex-col items-center gap-4 max-w-6xl mt-10 w-full mx-auto">
|
166 |
+
<h2 className="text-4xl font-bold text-center">Finetuned Model Growth</h2>
|
167 |
+
<p className="text-center mb-4">Find how many finetuned models have been created for your favorite model</p>
|
168 |
+
<form onSubmit={handleBaseModelSubmit} className="flex flex-col gap-2 w-full max-w-sm">
|
169 |
+
<input
|
170 |
+
type="text"
|
171 |
+
value={baseModel}
|
172 |
+
onChange={(e) => setBaseModel(e.target.value)}
|
173 |
+
placeholder="Base Model Name"
|
174 |
+
className="px-4 w-full py-2 border rounded"
|
175 |
+
/>
|
176 |
+
<Button type="submit" disabled={isLoading} className="w-full">
|
177 |
+
{isLoading ? (
|
178 |
+
<>
|
179 |
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
180 |
+
Loading
|
181 |
+
</>
|
182 |
+
) : (
|
183 |
+
"Submit"
|
184 |
+
)}
|
185 |
+
</Button>
|
186 |
+
</form>
|
187 |
+
</div>
|
188 |
+
|
189 |
+
{finetuneModelGrowthData.length > 0 && (
|
190 |
+
<div className="flex flex-col gap-4 max-w-4xl mt-10 w-full mx-auto">
|
191 |
+
<SimpleArea
|
192 |
+
title="Finetune Model Growth"
|
193 |
+
description={`Showing the growth of finetune models over time for ${baseModel || "your favorite model."}`}
|
194 |
+
data={finetuneModelGrowthData}
|
195 |
+
/>
|
196 |
+
</div>
|
197 |
+
)}
|
198 |
</section>
|
199 |
)
|
200 |
}
|
components/{area-chart.tsx → area-chart-stacked.tsx}
RENAMED
@@ -60,62 +60,68 @@ export function AreaChartStacked({ data }: AreaChartStackedProps) {
|
|
60 |
</CardDescription>
|
61 |
</CardHeader>
|
62 |
<CardContent>
|
63 |
-
|
64 |
-
<
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
>
|
72 |
-
<CartesianGrid vertical={false} />
|
73 |
-
<XAxis
|
74 |
-
dataKey="month"
|
75 |
-
tickLine={false}
|
76 |
-
axisLine={false}
|
77 |
-
tickMargin={8}
|
78 |
-
tickFormatter={(value) => {
|
79 |
-
const date = new Date(value)
|
80 |
-
return date.toLocaleString("default", {
|
81 |
-
month: "short",
|
82 |
-
year: "numeric",
|
83 |
-
})
|
84 |
}}
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
</CardContent>
|
120 |
</Card>
|
121 |
)
|
|
|
60 |
</CardDescription>
|
61 |
</CardHeader>
|
62 |
<CardContent>
|
63 |
+
{data.length > 0 ? (
|
64 |
+
<ChartContainer config={chartConfig}>
|
65 |
+
<AreaChart
|
66 |
+
accessibilityLayer
|
67 |
+
data={sortedData}
|
68 |
+
margin={{
|
69 |
+
left: 12,
|
70 |
+
right: 12,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
}}
|
72 |
+
>
|
73 |
+
<CartesianGrid vertical={false} />
|
74 |
+
<XAxis
|
75 |
+
dataKey="month"
|
76 |
+
tickLine={false}
|
77 |
+
axisLine={false}
|
78 |
+
tickMargin={8}
|
79 |
+
tickFormatter={(value) => {
|
80 |
+
const date = new Date(value)
|
81 |
+
return date.toLocaleString("default", {
|
82 |
+
month: "short",
|
83 |
+
year: "numeric",
|
84 |
+
})
|
85 |
+
}}
|
86 |
+
/>
|
87 |
+
<ChartTooltip
|
88 |
+
cursor={true}
|
89 |
+
content={<ChartTooltipContent indicator="line" hideLabel />}
|
90 |
+
/>
|
91 |
+
<Area
|
92 |
+
dataKey="spaces"
|
93 |
+
type="natural"
|
94 |
+
fill="hsl(var(--chart-3))"
|
95 |
+
fillOpacity={0.4}
|
96 |
+
stroke="hsl(var(--chart-3))"
|
97 |
+
stackId="a"
|
98 |
+
/>
|
99 |
+
<Area
|
100 |
+
dataKey="datasets"
|
101 |
+
type="natural"
|
102 |
+
fill="hsl(var(--chart-2))"
|
103 |
+
fillOpacity={0.2}
|
104 |
+
stroke="hsl(var(--chart-2))"
|
105 |
+
stackId="a"
|
106 |
+
/>
|
107 |
+
<Area
|
108 |
+
dataKey="models"
|
109 |
+
type="natural"
|
110 |
+
fill="hsl(var(--chart-1))"
|
111 |
+
fillOpacity={0.4}
|
112 |
+
stroke="hsl(var(--chart-1))"
|
113 |
+
stackId="a"
|
114 |
+
/>
|
115 |
+
<ChartLegend
|
116 |
+
content={<ChartLegendContent className="text-white" />}
|
117 |
+
/>
|
118 |
+
</AreaChart>
|
119 |
+
</ChartContainer>
|
120 |
+
) : (
|
121 |
+
<div className="flex items-center justify-center h-[300px] text-[var(--card-text)]">
|
122 |
+
Loading...
|
123 |
+
</div>
|
124 |
+
)}
|
125 |
</CardContent>
|
126 |
</Card>
|
127 |
)
|
components/pie-chart.tsx
CHANGED
@@ -69,31 +69,37 @@ export function CustomPieChart({
|
|
69 |
)}
|
70 |
</CardHeader>
|
71 |
<CardContent className="flex-1 pb-0">
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
<
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
<Pie data={chartData} dataKey={dataKey} nameKey="name">
|
82 |
-
{chartData.map((entry, index) => (
|
83 |
-
<Cell
|
84 |
-
key={`cell-${index}`}
|
85 |
-
fill={chartColors[index % chartColors.length]}
|
86 |
-
/>
|
87 |
-
))}
|
88 |
-
<LabelList
|
89 |
-
dataKey="name"
|
90 |
-
className="fill-background"
|
91 |
-
stroke="none"
|
92 |
-
fontSize={12}
|
93 |
/>
|
94 |
-
|
95 |
-
|
96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
</CardContent>
|
98 |
</Card>
|
99 |
)
|
|
|
69 |
)}
|
70 |
</CardHeader>
|
71 |
<CardContent className="flex-1 pb-0">
|
72 |
+
{data.length > 0 ? (
|
73 |
+
<ChartContainer
|
74 |
+
config={chartConfig}
|
75 |
+
className="mx-auto aspect-square max-h-[500px]"
|
76 |
+
>
|
77 |
+
<PieChart>
|
78 |
+
<ChartTooltip
|
79 |
+
cursor={false}
|
80 |
+
content={<ChartTooltipContent hideIndicator hideLabel />}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
/>
|
82 |
+
<Pie data={chartData} dataKey={dataKey} nameKey="name">
|
83 |
+
{chartData.map((entry, index) => (
|
84 |
+
<Cell
|
85 |
+
key={`cell-${index}`}
|
86 |
+
fill={chartColors[index % chartColors.length]}
|
87 |
+
/>
|
88 |
+
))}
|
89 |
+
<LabelList
|
90 |
+
dataKey="name"
|
91 |
+
className="fill-background"
|
92 |
+
stroke="none"
|
93 |
+
fontSize={12}
|
94 |
+
/>
|
95 |
+
</Pie>
|
96 |
+
</PieChart>
|
97 |
+
</ChartContainer>
|
98 |
+
) : (
|
99 |
+
<div className="flex items-center justify-center h-[300px] text-[var(--card-text)]">
|
100 |
+
Loading...
|
101 |
+
</div>
|
102 |
+
)}
|
103 |
</CardContent>
|
104 |
</Card>
|
105 |
)
|
components/simple-area.tsx
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client"
|
2 |
+
|
3 |
+
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
|
4 |
+
|
5 |
+
import {
|
6 |
+
Card,
|
7 |
+
CardContent,
|
8 |
+
CardDescription,
|
9 |
+
CardHeader,
|
10 |
+
CardTitle,
|
11 |
+
} from "@/components/ui/card"
|
12 |
+
import {
|
13 |
+
ChartConfig,
|
14 |
+
ChartContainer,
|
15 |
+
ChartTooltip,
|
16 |
+
ChartTooltipContent,
|
17 |
+
} from "@/components/ui/chart"
|
18 |
+
|
19 |
+
interface SimpleAreaProps {
|
20 |
+
title: string
|
21 |
+
description: string
|
22 |
+
data: Array<{ date: Date; count: number }>
|
23 |
+
}
|
24 |
+
|
25 |
+
export function SimpleArea({ title, description, data }: SimpleAreaProps) {
|
26 |
+
const chartConfig = {
|
27 |
+
count: {
|
28 |
+
label: "Total",
|
29 |
+
color: "hsl(var(--chart-1))",
|
30 |
+
},
|
31 |
+
} satisfies ChartConfig
|
32 |
+
|
33 |
+
const formattedData = data.map(item => ({
|
34 |
+
label: item.date.toISOString().split('T')[0],
|
35 |
+
count: item.count
|
36 |
+
}))
|
37 |
+
|
38 |
+
const total = data.length > 0 ? data[data.length - 1].count : 0
|
39 |
+
|
40 |
+
return (
|
41 |
+
<Card className="bg-[var(--card-background)]">
|
42 |
+
<CardHeader className="flex flex-row items-center justify-between">
|
43 |
+
<div>
|
44 |
+
<CardTitle className="text-[var(--card-text)]">{title}</CardTitle>
|
45 |
+
<CardDescription className="text-[var(--card-text)]">
|
46 |
+
{description}
|
47 |
+
</CardDescription>
|
48 |
+
</div>
|
49 |
+
<div className="text-right">
|
50 |
+
<span className="text-xs text-white">Total</span>
|
51 |
+
<p className="text-2xl font-bold text-white">{total.toLocaleString()}</p>
|
52 |
+
</div>
|
53 |
+
</CardHeader>
|
54 |
+
<CardContent>
|
55 |
+
{data.length > 0 ? (
|
56 |
+
<ChartContainer config={chartConfig}>
|
57 |
+
<AreaChart
|
58 |
+
accessibilityLayer
|
59 |
+
data={formattedData}
|
60 |
+
margin={{
|
61 |
+
left: 12,
|
62 |
+
right: 12,
|
63 |
+
}}
|
64 |
+
>
|
65 |
+
<CartesianGrid vertical={false} />
|
66 |
+
<XAxis
|
67 |
+
dataKey="label"
|
68 |
+
tickLine={false}
|
69 |
+
axisLine={false}
|
70 |
+
tickMargin={8}
|
71 |
+
tickFormatter={(value) => {
|
72 |
+
const date = new Date(value);
|
73 |
+
return `${date.getMonth() + 1}/${date.getDate()}`;
|
74 |
+
}}
|
75 |
+
/>
|
76 |
+
<ChartTooltip
|
77 |
+
cursor={false}
|
78 |
+
content={<ChartTooltipContent indicator="line" />}
|
79 |
+
/>
|
80 |
+
<Area
|
81 |
+
dataKey="count"
|
82 |
+
type="natural"
|
83 |
+
fill="hsl(var(--chart-1))"
|
84 |
+
fillOpacity={0.4}
|
85 |
+
stroke="hsl(var(--chart-1))"
|
86 |
+
/>
|
87 |
+
</AreaChart>
|
88 |
+
</ChartContainer>
|
89 |
+
) : (
|
90 |
+
<div className="flex items-center justify-center h-[300px] text-[var(--card-text)]">
|
91 |
+
Loading...
|
92 |
+
</div>
|
93 |
+
)}
|
94 |
+
</CardContent>
|
95 |
+
</Card>
|
96 |
+
)
|
97 |
+
}
|
lib/queries.ts
CHANGED
@@ -41,3 +41,29 @@ export const FETCH_SPACE_SDK_DATA_QUERY = `
|
|
41 |
FROM spaces
|
42 |
GROUP BY sdk;
|
43 |
`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
FROM spaces
|
42 |
GROUP BY sdk;
|
43 |
`
|
44 |
+
|
45 |
+
export const FETCH_FINETUNE_MODEL_GROWTH_QUERY = (baseModel: string) => `
|
46 |
+
WITH RECURSIVE month_series AS (
|
47 |
+
SELECT DATE_TRUNC('month', MIN(CAST(createdAt AS TIMESTAMP))) AS month
|
48 |
+
FROM models, UNNEST(tags) AS t(tag)
|
49 |
+
WHERE tag = 'base_model:${baseModel}'
|
50 |
+
|
51 |
+
UNION ALL
|
52 |
+
|
53 |
+
SELECT month + INTERVAL 1 MONTH
|
54 |
+
FROM month_series
|
55 |
+
WHERE month < DATE_TRUNC('month', CURRENT_DATE)
|
56 |
+
),
|
57 |
+
finetuned_models AS (
|
58 |
+
SELECT DATE_TRUNC('month', CAST(createdAt AS TIMESTAMP)) AS creation_month
|
59 |
+
FROM models, UNNEST(tags) AS t(tag)
|
60 |
+
WHERE tag = 'base_model:${baseModel}'
|
61 |
+
)
|
62 |
+
SELECT
|
63 |
+
strftime(ms.month, '%Y-%m') as date,
|
64 |
+
COALESCE(SUM(COUNT(fm.creation_month)) OVER (ORDER BY ms.month), 0) AS count
|
65 |
+
FROM month_series ms
|
66 |
+
LEFT JOIN finetuned_models fm ON ms.month = fm.creation_month
|
67 |
+
GROUP BY ms.month
|
68 |
+
ORDER BY ms.month
|
69 |
+
`
|