Skip to content

Commit 3874b50

Browse files
[Workers AI] Surface model capabilities (reasoning, vision) and add NVIDIA/Zhipu AI logos (#29111)
* [Workers AI] Surface model capabilities (reasoning, vision) and add NVIDIA/Zhipu AI logos - Sync model JSONs: add reasoning property on 7 models, vision on 2 models - Create shared model-properties utility to centralize capability definitions - Refactor ModelCatalog, ModelBadges to use shared utility (DRY, data-driven) - Add Reasoning and Vision rows to ModelFeatures info table - Add NVIDIA and Zhipu AI author logos and catalog entries * Update src/util/model-properties.ts Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com> --------- Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
1 parent 4aaeb61 commit 3874b50

17 files changed

+274
-127
lines changed
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 6 additions & 0 deletions
Loading

src/components/ModelCatalog.tsx

Lines changed: 96 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import ModelBadges from "./models/ModelBadges";
44
import { authorData } from "./models/data";
55
import type { WorkersAIModelsSchema } from "~/schemas";
66
import { setSearchParams } from "~/util/url";
7+
import {
8+
getCapabilities,
9+
getLabelsByCategory,
10+
hasProperty,
11+
} from "~/util/model-properties";
712

813
type Filters = {
914
search: string;
@@ -25,7 +30,6 @@ const ModelCatalog = ({ models }: { models: WorkersAIModelsSchema[] }) => {
2530
"@cf/openai/gpt-oss-120b",
2631
"@cf/openai/gpt-oss-20b",
2732
"@cf/meta/llama-4-scout-17b-16e-instruct",
28-
"@cf/meta/llama-3.3-70b-instruct-fp8-fast",
2933
"@cf/meta/llama-3.1-8b-instruct-fast",
3034
];
3135

@@ -97,52 +101,15 @@ const ModelCatalog = ({ models }: { models: WorkersAIModelsSchema[] }) => {
97101
const mapped = sortedModels.map((model) => ({
98102
model: {
99103
...model,
100-
capabilities: model.properties
101-
.flatMap(({ property_id, value }) => {
102-
if (property_id === "lora" && value === "true") {
103-
return "LoRA";
104-
}
105-
106-
if (property_id === "function_calling" && value === "true") {
107-
return "Function calling";
108-
}
109-
110-
if (property_id === "async_queue" && value === "true") {
111-
return "Batch";
112-
}
113-
114-
return [];
115-
})
116-
.filter((p) => Boolean(p)),
104+
capabilities: getCapabilities(model.properties),
117105
},
118106
model_display_name: model.name.split("/").at(-1),
119107
}));
120108

121109
const tasks = [...new Set(models.map((model) => model.task.name))];
122110
const authors = [...new Set(models.map((model) => model.name.split("/")[1]))];
123-
const capabilities = [
124-
...new Set(
125-
models.flatMap((model) =>
126-
model.properties
127-
.flatMap(({ property_id, value }) => {
128-
if (property_id === "lora" && value === "true") {
129-
return "LoRA";
130-
}
131-
132-
if (property_id === "function_calling" && value === "true") {
133-
return "Function calling";
134-
}
135-
136-
if (property_id === "async_queue" && value === "true") {
137-
return "Batch";
138-
}
139-
140-
return [];
141-
})
142-
.filter((p) => Boolean(p)),
143-
),
144-
),
145-
];
111+
const modelProperties = getLabelsByCategory(models, "model");
112+
const platformProperties = getLabelsByCategory(models, "platform");
146113

147114
const modelList = mapped.filter(({ model }) => {
148115
if (filters.authors.length > 0) {
@@ -183,10 +150,10 @@ const ModelCatalog = ({ models }: { models: WorkersAIModelsSchema[] }) => {
183150
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
184151
/>
185152

186-
<div className="mb-8! hidden md:block">
187-
<span className="text-sm font-bold text-gray-600 uppercase dark:text-gray-200">
188-
Tasks
189-
</span>
153+
<details className="mb-6! hidden md:block">
154+
<summary className="cursor-pointer text-sm font-bold text-gray-600 uppercase select-none dark:text-gray-200">
155+
Task Type
156+
</summary>
190157

191158
{tasks.map((task) => (
192159
<label key={task} className="my-2! block">
@@ -214,47 +181,88 @@ const ModelCatalog = ({ models }: { models: WorkersAIModelsSchema[] }) => {
214181
{task}
215182
</label>
216183
))}
217-
</div>
184+
</details>
218185

219-
<div className="mb-8! hidden md:block">
220-
<span className="text-sm font-bold text-gray-600 uppercase dark:text-gray-200">
186+
<details className="mb-6! hidden md:block">
187+
<summary className="cursor-pointer text-sm font-bold text-gray-600 uppercase select-none dark:text-gray-200">
221188
Capabilities
222-
</span>
223-
224-
{capabilities.map((capability) => (
225-
<label key={capability} className="my-2! block">
226-
<input
227-
type="checkbox"
228-
value={capability}
229-
checked={filters.capabilities.includes(capability)}
230-
className="mr-2"
231-
onChange={(e) => {
232-
const target = e.target as HTMLInputElement;
233-
234-
if (target.checked) {
235-
setFilters({
236-
...filters,
237-
capabilities: [...filters.capabilities, target.value],
238-
});
239-
} else {
240-
setFilters({
241-
...filters,
242-
capabilities: filters.capabilities.filter(
243-
(f) => f !== target.value,
244-
),
245-
});
246-
}
247-
}}
248-
/>{" "}
249-
{capability}
250-
</label>
251-
))}
252-
</div>
253-
254-
<div className="hidden md:block">
255-
<span className="text-sm font-bold text-gray-600 uppercase dark:text-gray-200">
189+
</summary>
190+
191+
{modelProperties.length > 0 && (
192+
<>
193+
<span className="mt-3! mb-1! block text-xs font-semibold text-gray-500 dark:text-gray-400">
194+
Model
195+
</span>
196+
{modelProperties.map((prop) => (
197+
<label key={prop} className="my-2! block">
198+
<input
199+
type="checkbox"
200+
value={prop}
201+
checked={filters.capabilities.includes(prop)}
202+
className="mr-2"
203+
onChange={(e) => {
204+
const target = e.target as HTMLInputElement;
205+
if (target.checked) {
206+
setFilters({
207+
...filters,
208+
capabilities: [...filters.capabilities, target.value],
209+
});
210+
} else {
211+
setFilters({
212+
...filters,
213+
capabilities: filters.capabilities.filter(
214+
(f) => f !== target.value,
215+
),
216+
});
217+
}
218+
}}
219+
/>{" "}
220+
{prop}
221+
</label>
222+
))}
223+
</>
224+
)}
225+
226+
{platformProperties.length > 0 && (
227+
<>
228+
<span className="mt-3! mb-1! block text-xs font-semibold text-gray-500 dark:text-gray-400">
229+
Platform
230+
</span>
231+
{platformProperties.map((prop) => (
232+
<label key={prop} className="my-2! block">
233+
<input
234+
type="checkbox"
235+
value={prop}
236+
checked={filters.capabilities.includes(prop)}
237+
className="mr-2"
238+
onChange={(e) => {
239+
const target = e.target as HTMLInputElement;
240+
if (target.checked) {
241+
setFilters({
242+
...filters,
243+
capabilities: [...filters.capabilities, target.value],
244+
});
245+
} else {
246+
setFilters({
247+
...filters,
248+
capabilities: filters.capabilities.filter(
249+
(f) => f !== target.value,
250+
),
251+
});
252+
}
253+
}}
254+
/>{" "}
255+
{prop}
256+
</label>
257+
))}
258+
</>
259+
)}
260+
</details>
261+
262+
<details className="mb-6! hidden md:block">
263+
<summary className="cursor-pointer text-sm font-bold text-gray-600 uppercase select-none dark:text-gray-200">
256264
Authors
257-
</span>
265+
</summary>
258266

259267
{authors.map((author) => (
260268
<label key={author} className="my-2! block">
@@ -284,7 +292,7 @@ const ModelCatalog = ({ models }: { models: WorkersAIModelsSchema[] }) => {
284292
{authorData[author] ? authorData[author].name : author}
285293
</label>
286294
))}
287-
</div>
295+
</details>
288296
</div>
289297
<div className="mt-0! flex w-full flex-wrap items-stretch gap-[1%] self-start md:w-3/4">
290298
{modelList.length === 0 && (
@@ -297,10 +305,7 @@ const ModelCatalog = ({ models }: { models: WorkersAIModelsSchema[] }) => {
297305
</div>
298306
)}
299307
{modelList.map((model) => {
300-
const isBeta = model.model.properties.find(
301-
({ property_id, value }) =>
302-
property_id === "beta" && value === "true",
303-
);
308+
const isBeta = hasProperty(model.model.properties, "beta");
304309

305310
const author = model.model.name.split("/")[1];
306311
const authorInfo = authorData[author];
@@ -309,7 +314,7 @@ const ModelCatalog = ({ models }: { models: WorkersAIModelsSchema[] }) => {
309314
return (
310315
<a
311316
key={model.model.id}
312-
className="relative mb-3 block w-full self-start rounded-md border border-solid border-gray-200 p-3 text-inherit! no-underline hover:bg-gray-50 lg:w-[48%] dark:border-gray-700 dark:hover:bg-gray-800"
317+
className="relative mb-3 flex w-full flex-col rounded-md border border-solid border-gray-200 p-3 text-inherit! no-underline hover:bg-gray-50 lg:w-[48%] dark:border-gray-700 dark:hover:bg-gray-800"
313318
href={`/workers-ai/models/${model.model_display_name}`}
314319
>
315320
{isPinned && (
@@ -337,10 +342,10 @@ const ModelCatalog = ({ models }: { models: WorkersAIModelsSchema[] }) => {
337342
<div className="m-0! text-xs">
338343
<ModelInfo model={model.model} />
339344
</div>
340-
<p className="mt-2! line-clamp-2 text-sm leading-6">
345+
<p className="mt-2! line-clamp-2 flex-1 text-sm leading-6">
341346
{model.model.description}
342347
</p>
343-
<div className="mt-2! text-xs">
348+
<div className="mt-2! min-h-6 text-xs">
344349
<ModelBadges model={model.model} />
345350
</div>
346351
</a>

src/components/models/ModelBadges.tsx

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,23 @@
11
import type { WorkersAIModelsSchema } from "~/schemas";
2+
import { CAPABILITY_PROPERTIES } from "~/util/model-properties";
3+
4+
const CATEGORY_BADGE: Record<string, string> = {
5+
model: "default", // gray
6+
platform: "caution", // orange
7+
};
28

39
const ModelBadges = ({ model }: { model: WorkersAIModelsSchema }) => {
410
const badges = model.properties.flatMap(({ property_id, value }) => {
5-
if (property_id === "lora" && value === "true") {
6-
return {
7-
variant: "tip",
8-
text: "LoRA",
9-
};
10-
}
11-
12-
if (property_id === "function_calling" && value === "true") {
13-
return {
14-
variant: "note",
15-
text: "Function calling",
16-
};
17-
}
18-
19-
if (property_id === "async_queue" && value === "true") {
20-
return {
21-
variant: "note",
22-
text: "Batch",
23-
};
24-
}
25-
26-
if (property_id === "partner" && value === "true") {
27-
return {
28-
variant: "note",
29-
text: "Partner",
30-
};
31-
}
32-
33-
if (property_id === "realtime" && value === "true") {
11+
// Boolean capability badges (data-driven)
12+
if (property_id in CAPABILITY_PROPERTIES && value === "true") {
13+
const def = CAPABILITY_PROPERTIES[property_id];
3414
return {
35-
variant: "note",
36-
text: "Real-time",
15+
variant: CATEGORY_BADGE[def.category] ?? "default",
16+
text: def.label,
3717
};
3818
}
3919

20+
// Special case: deprecation badge (not a boolean capability)
4021
if (property_id === "planned_deprecation_date") {
4122
const timestamp = Math.floor(new Date(value as string).getTime());
4223

@@ -51,10 +32,10 @@ const ModelBadges = ({ model }: { model: WorkersAIModelsSchema }) => {
5132
});
5233

5334
return (
54-
<ul className="m-0 flex list-none items-center gap-2 p-0 text-xs">
35+
<ul className="m-0 flex list-none flex-wrap items-center gap-1.5 p-0 text-xs [&>li]:m-0">
5536
{badges.map((badge) => (
5637
<li key={badge.text}>
57-
<span className="sl-badge default">{badge.text}</span>
38+
<span className={`sl-badge ${badge.variant}`}>{badge.text}</span>
5839
</li>
5940
))}
6041
</ul>

src/components/models/ModelFeatures.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ const ModelFeatures = ({ model }: { model: WorkersAIModelsSchema }) => {
9797
<td>Yes</td>
9898
</tr>
9999
)}
100+
{properties.reasoning && (
101+
<tr>
102+
<td>Reasoning</td>
103+
<td>Yes</td>
104+
</tr>
105+
)}
106+
{properties.vision && (
107+
<tr>
108+
<td>Vision</td>
109+
<td>Yes</td>
110+
</tr>
111+
)}
100112
{properties.lora && (
101113
<tr>
102114
<td>LoRA</td>

src/components/models/data.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import blackforestlabs from "../../assets/images/workers-ai/blackforestlabs.svg"
1111
import deepgram from "../../assets/images/workers-ai/deepgram.svg";
1212
import leonardo from "../../assets/images/workers-ai/leonardo.svg";
1313
import ibm from "../../assets/images/workers-ai/ibm.svg";
14+
import nvidia from "../../assets/images/workers-ai/nvidia.svg";
15+
import zaiorg from "../../assets/images/workers-ai/zai-org.svg";
1416

1517
export const authorData: Record<string, { name: string; logo: string }> = {
1618
openai: {
@@ -69,4 +71,12 @@ export const authorData: Record<string, { name: string; logo: string }> = {
6971
name: "IBM",
7072
logo: ibm.src,
7173
},
74+
nvidia: {
75+
name: "NVIDIA",
76+
logo: nvidia.src,
77+
},
78+
"zai-org": {
79+
name: "Zhipu AI",
80+
logo: zaiorg.src,
81+
},
7282
};

src/content/workers-ai-models/deepseek-r1-distill-qwen-32b.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
}
3131
]
3232
},
33+
{
34+
"property_id": "reasoning",
35+
"value": "true"
36+
},
3337
{
3438
"property_id": "terms",
3539
"value": "https://github.com/deepseek-ai/DeepSeek-R1/blob/main/LICENSE"

0 commit comments

Comments
 (0)