Commit
•
22aa376
1
Parent(s):
b6b0a70
add spaces, datasets created
Browse files- src/components/Heatmap.tsx +50 -0
- src/pages/[author]/index.tsx +26 -38
- src/pages/index.tsx +59 -57
- src/styles/globals.css +1 -1
- src/utils/calendar.ts +67 -62
src/components/Heatmap.tsx
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from "react";
|
2 |
+
import ActivityCalendar from "react-activity-calendar";
|
3 |
+
import { Tooltip } from "@mui/material";
|
4 |
+
import Link from "next/link";
|
5 |
+
|
6 |
+
type HeatmapProps = {
|
7 |
+
data: Array<{ date: string; count: number; level: number }>;
|
8 |
+
color: string;
|
9 |
+
providerName: string;
|
10 |
+
};
|
11 |
+
|
12 |
+
const Heatmap: React.FC<HeatmapProps> = ({ data, color, providerName }) => {
|
13 |
+
return (
|
14 |
+
<div className="flex flex-col items-center">
|
15 |
+
<div className="w-full overflow-x-auto flex justify-center">
|
16 |
+
<ActivityCalendar
|
17 |
+
data={data}
|
18 |
+
theme={{
|
19 |
+
dark: ["#161b22", color],
|
20 |
+
light: ["#e0e0e0", color],
|
21 |
+
}}
|
22 |
+
hideTotalCount
|
23 |
+
renderBlock={(block, activity) => (
|
24 |
+
<Tooltip
|
25 |
+
title={`${activity.count} events on ${activity.date}`}
|
26 |
+
arrow
|
27 |
+
>
|
28 |
+
{block}
|
29 |
+
</Tooltip>
|
30 |
+
)}
|
31 |
+
/>
|
32 |
+
</div>
|
33 |
+
<div>
|
34 |
+
<p className="text-sm italic light:text-slate-500">
|
35 |
+
Models, Datasets, and Spaces created by{" "}
|
36 |
+
<Link
|
37 |
+
href={`https://huggingface.co/${providerName}`}
|
38 |
+
target="_blank"
|
39 |
+
rel="noopener noreferrer"
|
40 |
+
className="hover:underline text-blue-500"
|
41 |
+
>
|
42 |
+
{providerName}.
|
43 |
+
</Link>
|
44 |
+
</p>
|
45 |
+
</div>
|
46 |
+
</div>
|
47 |
+
);
|
48 |
+
};
|
49 |
+
|
50 |
+
export default Heatmap;
|
src/pages/[author]/index.tsx
CHANGED
@@ -1,11 +1,10 @@
|
|
1 |
import React, { useState, useEffect } from "react";
|
2 |
-
import ActivityCalendar from "react-activity-calendar";
|
3 |
-
import { Tooltip } from "@mui/material";
|
4 |
import { GetServerSidePropsContext } from "next";
|
5 |
-
import {
|
6 |
import { generateCalendarData } from "../../utils/calendar";
|
|
|
7 |
|
8 |
-
const DEFAULT_COLOR = "#
|
9 |
|
10 |
const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
|
11 |
calendarData,
|
@@ -21,39 +20,23 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
|
|
21 |
|
22 |
return (
|
23 |
<div className="w-full max-w-screen-lg mx-auto p-4">
|
24 |
-
|
25 |
{isLoading ? (
|
26 |
<p className="text-center">Loading...</p>
|
27 |
) : (
|
28 |
-
<div
|
29 |
{Object.entries(providers)
|
30 |
.sort(
|
31 |
([keyA], [keyB]) =>
|
32 |
calendarData[keyB].reduce((sum, day) => sum + day.count, 0) -
|
33 |
-
calendarData[keyA].reduce((sum, day) => sum + day.count, 0)
|
34 |
)
|
35 |
.map(([providerName, { color }]) => (
|
36 |
-
<
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
dark: ["#161b22", color],
|
43 |
-
light: ["#e0e0e0", color],
|
44 |
-
}}
|
45 |
-
hideTotalCount
|
46 |
-
renderBlock={(block, activity) => (
|
47 |
-
<Tooltip
|
48 |
-
title={`${activity.count} models created on ${activity.date}`}
|
49 |
-
arrow
|
50 |
-
>
|
51 |
-
{block}
|
52 |
-
</Tooltip>
|
53 |
-
)}
|
54 |
-
/>
|
55 |
-
</div>
|
56 |
-
</div>
|
57 |
))}
|
58 |
</div>
|
59 |
)}
|
@@ -74,17 +57,23 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|
74 |
};
|
75 |
|
76 |
try {
|
77 |
-
const
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
);
|
80 |
-
const data = await response.json();
|
81 |
-
|
82 |
-
const modelData = data.map((item: any) => ({
|
83 |
-
createdAt: item.createdAt,
|
84 |
-
id: item.id,
|
85 |
-
}));
|
86 |
|
87 |
-
const
|
|
|
88 |
|
89 |
return {
|
90 |
props: {
|
@@ -105,5 +94,4 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|
105 |
}
|
106 |
}
|
107 |
|
108 |
-
|
109 |
export default OpenSourceHeatmap;
|
|
|
1 |
import React, { useState, useEffect } from "react";
|
|
|
|
|
2 |
import { GetServerSidePropsContext } from "next";
|
3 |
+
import { OpenSourceHeatmapProps } from "../../types/heatmap";
|
4 |
import { generateCalendarData } from "../../utils/calendar";
|
5 |
+
import Heatmap from "../../components/Heatmap";
|
6 |
|
7 |
+
const DEFAULT_COLOR = "#FF9D00";
|
8 |
|
9 |
const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
|
10 |
calendarData,
|
|
|
20 |
|
21 |
return (
|
22 |
<div className="w-full max-w-screen-lg mx-auto p-4">
|
|
|
23 |
{isLoading ? (
|
24 |
<p className="text-center">Loading...</p>
|
25 |
) : (
|
26 |
+
<div>
|
27 |
{Object.entries(providers)
|
28 |
.sort(
|
29 |
([keyA], [keyB]) =>
|
30 |
calendarData[keyB].reduce((sum, day) => sum + day.count, 0) -
|
31 |
+
calendarData[keyA].reduce((sum, day) => sum + day.count, 0)
|
32 |
)
|
33 |
.map(([providerName, { color }]) => (
|
34 |
+
<Heatmap
|
35 |
+
key={providerName}
|
36 |
+
data={calendarData[providerName]}
|
37 |
+
color={color}
|
38 |
+
providerName={providerName}
|
39 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
))}
|
41 |
</div>
|
42 |
)}
|
|
|
57 |
};
|
58 |
|
59 |
try {
|
60 |
+
const entityTypes = ["models", "datasets", "spaces"];
|
61 |
+
const allData = await Promise.all(
|
62 |
+
entityTypes.map(async (type) => {
|
63 |
+
const response = await fetch(
|
64 |
+
`https://huggingface.co/api/${type}?author=${author}&sort=createdAt&direction=-1`,
|
65 |
+
);
|
66 |
+
const data = await response.json();
|
67 |
+
return data.map((item: any) => ({
|
68 |
+
createdAt: item.createdAt,
|
69 |
+
id: item.id,
|
70 |
+
type: type,
|
71 |
+
}));
|
72 |
+
}),
|
73 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
+
const flatData = allData.flat();
|
76 |
+
const calendarData = generateCalendarData(flatData, providers);
|
77 |
|
78 |
return {
|
79 |
props: {
|
|
|
94 |
}
|
95 |
}
|
96 |
|
|
|
97 |
export default OpenSourceHeatmap;
|
src/pages/index.tsx
CHANGED
@@ -1,44 +1,55 @@
|
|
1 |
-
import React, { useState, useEffect } from
|
2 |
-
import ActivityCalendar from "react-activity-calendar";
|
3 |
-
import { Tooltip } from '@mui/material';
|
4 |
import { generateCalendarData } from "../utils/calendar";
|
5 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
6 |
|
7 |
const PROVIDERS_MAP: Record<string, ProviderInfo> = {
|
8 |
"Mistral AI": { color: "#ff7000", authors: ["mistralai"] },
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
"Allen Institute for AI": { color: "#5E35B1", authors: ["allenai"] },
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
"Cohere For AI": { color: "#4C6EE6", authors: ["CohereForAI"] },
|
20 |
-
|
21 |
"Stability AI": { color: "#A020F0", authors: ["stabilityai"] },
|
22 |
};
|
23 |
|
24 |
export async function getStaticProps() {
|
25 |
try {
|
26 |
-
const allAuthors = Object.values(PROVIDERS_MAP).flatMap(
|
|
|
|
|
27 |
const uniqueAuthors = Array.from(new Set(allAuthors));
|
28 |
|
29 |
-
const
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
);
|
39 |
|
40 |
-
const
|
41 |
-
const calendarData = generateCalendarData(
|
42 |
|
43 |
return {
|
44 |
props: {
|
@@ -59,7 +70,10 @@ export async function getStaticProps() {
|
|
59 |
}
|
60 |
}
|
61 |
|
62 |
-
const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
|
|
|
|
|
|
|
63 |
const [isLoading, setIsLoading] = useState(true);
|
64 |
|
65 |
useEffect(() => {
|
@@ -70,10 +84,12 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({ calendarData, pro
|
|
70 |
|
71 |
return (
|
72 |
<div className="w-full max-w-screen-lg mx-auto p-4">
|
73 |
-
<h1 className="text-3xl lg:text-5xl mt-16 font-bold text-center mb-2">
|
74 |
-
|
75 |
-
|
76 |
-
|
|
|
|
|
77 |
<a
|
78 |
href="https://huggingface.co/spaces/cfahlgren1/model-release-heatmap/discussions/new"
|
79 |
target="_blank"
|
@@ -87,40 +103,26 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({ calendarData, pro
|
|
87 |
{isLoading ? (
|
88 |
<p className="text-center">Loading...</p>
|
89 |
) : (
|
90 |
-
<div className="space-y-
|
91 |
{Object.entries(providers)
|
92 |
-
.sort(
|
93 |
-
|
94 |
-
|
|
|
95 |
)
|
96 |
.map(([providerName, { color }]) => (
|
97 |
<div key={providerName} className="flex flex-col items-center">
|
98 |
-
<
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
dark: ['#161b22', color],
|
104 |
-
light: ['#e0e0e0', color],
|
105 |
-
}}
|
106 |
-
hideTotalCount
|
107 |
-
renderBlock={(block, activity) => (
|
108 |
-
<Tooltip
|
109 |
-
title={`${activity.count} models created on ${activity.date}`}
|
110 |
-
arrow
|
111 |
-
>
|
112 |
-
{block}
|
113 |
-
</Tooltip>
|
114 |
-
)}
|
115 |
-
/>
|
116 |
-
</div>
|
117 |
</div>
|
118 |
-
))
|
119 |
-
}
|
120 |
</div>
|
121 |
)}
|
122 |
</div>
|
123 |
);
|
124 |
-
}
|
125 |
|
126 |
export default OpenSourceHeatmap;
|
|
|
1 |
+
import React, { useState, useEffect } from "react";
|
|
|
|
|
2 |
import { generateCalendarData } from "../utils/calendar";
|
3 |
+
import {
|
4 |
+
OpenSourceHeatmapProps,
|
5 |
+
ProviderInfo,
|
6 |
+
ModelData,
|
7 |
+
} from "../types/heatmap";
|
8 |
+
import Heatmap from "../components/Heatmap";
|
9 |
|
10 |
const PROVIDERS_MAP: Record<string, ProviderInfo> = {
|
11 |
"Mistral AI": { color: "#ff7000", authors: ["mistralai"] },
|
12 |
+
Meta: { color: "#1877F2", authors: ["facebook", "meta-llama"] },
|
13 |
+
OpenAI: { color: "#10A37F", authors: ["openai"] },
|
14 |
+
Anthropic: { color: "#cc785c", authors: ["Anthropic"] },
|
15 |
+
Google: { color: "#DB4437", authors: ["google"] },
|
16 |
"Allen Institute for AI": { color: "#5E35B1", authors: ["allenai"] },
|
17 |
+
Apple: { color: "#0088cc", authors: ["apple"] },
|
18 |
+
Microsoft: { color: "#FEB800", authors: ["microsoft"] },
|
19 |
+
NVIDIA: { color: "#76B900", authors: ["nvidia"] },
|
20 |
+
DeepSeek: { color: "#0088cc", authors: ["deepseek-ai"] },
|
21 |
+
Qwen: { color: "#0088cc", authors: ["Qwen"] },
|
22 |
"Cohere For AI": { color: "#4C6EE6", authors: ["CohereForAI"] },
|
23 |
+
IBM: { color: "#4C6EE6", authors: ["ibm-granite"] },
|
24 |
"Stability AI": { color: "#A020F0", authors: ["stabilityai"] },
|
25 |
};
|
26 |
|
27 |
export async function getStaticProps() {
|
28 |
try {
|
29 |
+
const allAuthors = Object.values(PROVIDERS_MAP).flatMap(
|
30 |
+
({ authors }) => authors,
|
31 |
+
);
|
32 |
const uniqueAuthors = Array.from(new Set(allAuthors));
|
33 |
|
34 |
+
const entityTypes = ["models", "datasets", "spaces"];
|
35 |
+
const allData = await Promise.all(
|
36 |
+
uniqueAuthors.flatMap((author) =>
|
37 |
+
entityTypes.map(async (type) => {
|
38 |
+
const response = await fetch(
|
39 |
+
`https://huggingface.co/api/${type}?author=${author}&sort=createdAt&direction=-1`,
|
40 |
+
);
|
41 |
+
const data = await response.json();
|
42 |
+
return data.map((item: any) => ({
|
43 |
+
createdAt: item.createdAt,
|
44 |
+
id: item.id,
|
45 |
+
type: type,
|
46 |
+
}));
|
47 |
+
}),
|
48 |
+
),
|
49 |
);
|
50 |
|
51 |
+
const flatData: ModelData[] = allData.flat();
|
52 |
+
const calendarData = generateCalendarData(flatData, PROVIDERS_MAP);
|
53 |
|
54 |
return {
|
55 |
props: {
|
|
|
70 |
}
|
71 |
}
|
72 |
|
73 |
+
const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
|
74 |
+
calendarData,
|
75 |
+
providers,
|
76 |
+
}) => {
|
77 |
const [isLoading, setIsLoading] = useState(true);
|
78 |
|
79 |
useEffect(() => {
|
|
|
84 |
|
85 |
return (
|
86 |
<div className="w-full max-w-screen-lg mx-auto p-4">
|
87 |
+
<h1 className="text-3xl lg:text-5xl mt-16 font-bold text-center mb-2">
|
88 |
+
Hugging Face Heatmap 🤗
|
89 |
+
</h1>
|
90 |
+
<p className="text-center text-sm my-8">
|
91 |
+
Models, Datasets, and Spaces from the top AI labs. <br />
|
92 |
+
Request more heatmaps by{" "}
|
93 |
<a
|
94 |
href="https://huggingface.co/spaces/cfahlgren1/model-release-heatmap/discussions/new"
|
95 |
target="_blank"
|
|
|
103 |
{isLoading ? (
|
104 |
<p className="text-center">Loading...</p>
|
105 |
) : (
|
106 |
+
<div className="space-y-16">
|
107 |
{Object.entries(providers)
|
108 |
+
.sort(
|
109 |
+
([keyA], [keyB]) =>
|
110 |
+
calendarData[keyB].reduce((sum, day) => sum + day.count, 0) -
|
111 |
+
calendarData[keyA].reduce((sum, day) => sum + day.count, 0),
|
112 |
)
|
113 |
.map(([providerName, { color }]) => (
|
114 |
<div key={providerName} className="flex flex-col items-center">
|
115 |
+
<Heatmap
|
116 |
+
data={calendarData[providerName]}
|
117 |
+
color={color}
|
118 |
+
providerName={providerName}
|
119 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
</div>
|
121 |
+
))}
|
|
|
122 |
</div>
|
123 |
)}
|
124 |
</div>
|
125 |
);
|
126 |
+
};
|
127 |
|
128 |
export default OpenSourceHeatmap;
|
src/styles/globals.css
CHANGED
@@ -23,4 +23,4 @@ body {
|
|
23 |
.text-balance {
|
24 |
text-wrap: balance;
|
25 |
}
|
26 |
-
}
|
|
|
23 |
.text-balance {
|
24 |
text-wrap: balance;
|
25 |
}
|
26 |
+
}
|
src/utils/calendar.ts
CHANGED
@@ -1,66 +1,71 @@
|
|
1 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
export const generateCalendarData = (
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
});
|
28 |
});
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
});
|
38 |
-
}
|
39 |
-
|
40 |
-
// calculate average counts for each provider
|
41 |
-
const avgCounts = Object.fromEntries(
|
42 |
-
Object.entries(data).map(([provider, days]) => [
|
43 |
-
provider,
|
44 |
-
days.reduce((sum, day) => sum + day.count, 0) / days.length || 0,
|
45 |
-
]),
|
46 |
-
);
|
47 |
-
|
48 |
-
// assign levels based on count relative to average
|
49 |
-
Object.entries(data).forEach(([provider, days]) => {
|
50 |
-
const avgCount = avgCounts[provider];
|
51 |
-
days.forEach((day) => {
|
52 |
-
day.level =
|
53 |
-
day.count === 0
|
54 |
-
? 0
|
55 |
-
: day.count <= avgCount * 0.5
|
56 |
-
? 1
|
57 |
-
: day.count <= avgCount
|
58 |
-
? 2
|
59 |
-
: day.count <= avgCount * 1.5
|
60 |
-
? 3
|
61 |
-
: 4;
|
62 |
-
});
|
63 |
});
|
64 |
-
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
ModelData,
|
3 |
+
ProviderInfo,
|
4 |
+
CalendarData,
|
5 |
+
Activity,
|
6 |
+
} from "../types/heatmap";
|
7 |
|
8 |
export const generateCalendarData = (
|
9 |
+
modelData: ModelData[],
|
10 |
+
providers: Record<string, ProviderInfo>,
|
11 |
+
): CalendarData => {
|
12 |
+
const data: Record<string, Activity[]> = Object.fromEntries(
|
13 |
+
Object.keys(providers).map((provider) => [provider, []]),
|
14 |
+
);
|
15 |
+
|
16 |
+
const today = new Date();
|
17 |
+
const startDate = new Date(today);
|
18 |
+
startDate.setMonth(today.getMonth() - 11);
|
19 |
+
startDate.setDate(1);
|
20 |
+
|
21 |
+
// create a map to store counts for each provider and date
|
22 |
+
const countMap: Record<string, Record<string, number>> = {};
|
23 |
+
|
24 |
+
modelData.forEach((item) => {
|
25 |
+
const dateString = item.createdAt.split("T")[0];
|
26 |
+
Object.entries(providers).forEach(([provider, { authors }]) => {
|
27 |
+
if (authors.some((author) => item.id.startsWith(author))) {
|
28 |
+
countMap[provider] = countMap[provider] || {};
|
29 |
+
countMap[provider][dateString] =
|
30 |
+
(countMap[provider][dateString] || 0) + 1;
|
31 |
+
}
|
|
|
32 |
});
|
33 |
+
});
|
34 |
+
|
35 |
+
// fill in with 0s for days with no activity
|
36 |
+
for (let d = new Date(startDate); d <= today; d.setDate(d.getDate() + 1)) {
|
37 |
+
const dateString = d.toISOString().split("T")[0];
|
38 |
+
|
39 |
+
Object.entries(providers).forEach(([provider]) => {
|
40 |
+
const count = countMap[provider]?.[dateString] || 0;
|
41 |
+
data[provider].push({ date: dateString, count, level: 0 });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
});
|
43 |
+
}
|
44 |
+
|
45 |
+
// calculate average counts for each provider
|
46 |
+
const avgCounts = Object.fromEntries(
|
47 |
+
Object.entries(data).map(([provider, days]) => [
|
48 |
+
provider,
|
49 |
+
days.reduce((sum, day) => sum + day.count, 0) / days.length || 0,
|
50 |
+
]),
|
51 |
+
);
|
52 |
+
|
53 |
+
// assign levels based on count relative to average
|
54 |
+
Object.entries(data).forEach(([provider, days]) => {
|
55 |
+
const avgCount = avgCounts[provider];
|
56 |
+
days.forEach((day) => {
|
57 |
+
day.level =
|
58 |
+
day.count === 0
|
59 |
+
? 0
|
60 |
+
: day.count <= avgCount * 0.5
|
61 |
+
? 1
|
62 |
+
: day.count <= avgCount
|
63 |
+
? 2
|
64 |
+
: day.count <= avgCount * 1.5
|
65 |
+
? 3
|
66 |
+
: 4;
|
67 |
+
});
|
68 |
+
});
|
69 |
+
|
70 |
+
return data;
|
71 |
+
};
|