@@ -4,6 +4,11 @@ import ModelBadges from "./models/ModelBadges";
44import { authorData } from "./models/data" ;
55import type { WorkersAIModelsSchema } from "~/schemas" ;
66import { setSearchParams } from "~/util/url" ;
7+ import {
8+ getCapabilities ,
9+ getLabelsByCategory ,
10+ hasProperty ,
11+ } from "~/util/model-properties" ;
712
813type 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 >
0 commit comments