Refactor model selection and info display
Browse files- Introduced ModelInfo component to encapsulate model details and stats.
- Updated App component to utilize ModelInfo and streamline model fetching logic.
- Enhanced ModelSelector with sorting functionality and improved UI using Headless UI Listbox.
- Implemented PipelineSelector with a similar UI enhancement.
- Removed redundant model info fetching from ZeroShotClassification.
- Updated ModelContext to manage selected quantization state.
- Enhanced getModelInfo function to include compatibility checks and supported quantizations.
- Updated types to reflect new model info structure and quantization types.
- package.json +1 -0
- pnpm-lock.yaml +196 -0
- src/App.tsx +29 -77
- src/components/ModelInfo.tsx +164 -0
- src/components/ModelSelector.tsx +198 -115
- src/components/PipelineSelector.tsx +74 -7
- src/components/ZeroShotClassification.tsx +0 -31
- src/contexts/ModelContext.tsx +6 -1
- src/lib/huggingface.ts +74 -12
- src/types.ts +24 -0
package.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
"version": "0.1.0",
|
| 4 |
"private": true,
|
| 5 |
"dependencies": {
|
|
|
|
| 6 |
"@huggingface/transformers": "^3.6.1",
|
| 7 |
"@testing-library/dom": "^10.4.0",
|
| 8 |
"@testing-library/jest-dom": "^6.6.3",
|
|
|
|
| 3 |
"version": "0.1.0",
|
| 4 |
"private": true,
|
| 5 |
"dependencies": {
|
| 6 |
+
"@headlessui/react": "^2.2.4",
|
| 7 |
"@huggingface/transformers": "^3.6.1",
|
| 8 |
"@testing-library/dom": "^10.4.0",
|
| 9 |
"@testing-library/jest-dom": "^6.6.3",
|
pnpm-lock.yaml
CHANGED
|
@@ -8,6 +8,9 @@ importers:
|
|
| 8 |
|
| 9 |
.:
|
| 10 |
dependencies:
|
|
|
|
|
|
|
|
|
|
| 11 |
'@huggingface/transformers':
|
| 12 |
specifier: ^3.6.1
|
| 13 |
version: 3.6.1
|
|
@@ -917,6 +920,34 @@ packages:
|
|
| 917 |
resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
|
| 918 |
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
| 919 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 920 |
'@huggingface/[email protected]':
|
| 921 |
resolution: {integrity: sha512-3WXbMFaPkk03LRCM0z0sylmn8ddDm4ubjU7X+Hg4M2GOuMklwoGAFXp9V2keq7vltoB/c7McE5aHUVVddAewsw==}
|
| 922 |
engines: {node: '>=18'}
|
|
@@ -1233,6 +1264,43 @@ packages:
|
|
| 1233 |
'@protobufjs/[email protected]':
|
| 1234 |
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
| 1235 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1236 |
'@rollup/[email protected]':
|
| 1237 |
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
|
| 1238 |
engines: {node: '>= 10.0.0'}
|
|
@@ -1335,6 +1403,18 @@ packages:
|
|
| 1335 |
resolution: {integrity: sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==}
|
| 1336 |
engines: {node: '>=10'}
|
| 1337 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1338 |
'@testing-library/[email protected]':
|
| 1339 |
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
|
| 1340 |
engines: {node: '>=18'}
|
|
@@ -2076,6 +2156,10 @@ packages:
|
|
| 2076 | |
| 2077 |
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
| 2078 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2079 | |
| 2080 |
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
| 2081 |
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
|
@@ -5377,6 +5461,9 @@ packages:
|
|
| 5377 | |
| 5378 |
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
| 5379 |
|
|
|
|
|
|
|
|
|
|
| 5380 | |
| 5381 |
resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
|
| 5382 |
engines: {node: '>=14.0.0'}
|
|
@@ -5619,6 +5706,11 @@ packages:
|
|
| 5619 | |
| 5620 |
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
| 5621 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5622 | |
| 5623 |
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
| 5624 |
|
|
@@ -6951,6 +7043,41 @@ snapshots:
|
|
| 6951 |
|
| 6952 |
'@eslint/[email protected]': {}
|
| 6953 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6954 |
'@huggingface/[email protected]': {}
|
| 6955 |
|
| 6956 |
'@huggingface/[email protected]':
|
|
@@ -7331,6 +7458,55 @@ snapshots:
|
|
| 7331 |
|
| 7332 |
'@protobufjs/[email protected]': {}
|
| 7333 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7334 |
'@rollup/[email protected](@babel/[email protected])(@types/[email protected])([email protected])':
|
| 7335 |
dependencies:
|
| 7336 |
'@babel/core': 7.28.0
|
|
@@ -7453,6 +7629,18 @@ snapshots:
|
|
| 7453 |
transitivePeerDependencies:
|
| 7454 |
- supports-color
|
| 7455 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7456 |
'@testing-library/[email protected]':
|
| 7457 |
dependencies:
|
| 7458 |
'@babel/code-frame': 7.27.1
|
|
@@ -8376,6 +8564,8 @@ snapshots:
|
|
| 8376 |
strip-ansi: 6.0.1
|
| 8377 |
wrap-ansi: 7.0.0
|
| 8378 |
|
|
|
|
|
|
|
| 8379 | |
| 8380 |
|
| 8381 | |
|
@@ -12287,6 +12477,8 @@ snapshots:
|
|
| 12287 |
|
| 12288 | |
| 12289 |
|
|
|
|
|
|
|
| 12290 | |
| 12291 |
dependencies:
|
| 12292 |
'@alloc/quick-lru': 5.2.0
|
|
@@ -12539,6 +12731,10 @@ snapshots:
|
|
| 12539 |
querystringify: 2.2.0
|
| 12540 |
requires-port: 1.0.0
|
| 12541 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12542 | |
| 12543 |
|
| 12544 |
|
|
|
| 8 |
|
| 9 |
.:
|
| 10 |
dependencies:
|
| 11 |
+
'@headlessui/react':
|
| 12 |
+
specifier: ^2.2.4
|
| 13 |
+
version: 2.2.4([email protected]([email protected]))([email protected])
|
| 14 |
'@huggingface/transformers':
|
| 15 |
specifier: ^3.6.1
|
| 16 |
version: 3.6.1
|
|
|
|
| 920 |
resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
|
| 921 |
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
| 922 |
|
| 923 |
+
'@floating-ui/[email protected]':
|
| 924 |
+
resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==}
|
| 925 |
+
|
| 926 |
+
'@floating-ui/[email protected]':
|
| 927 |
+
resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==}
|
| 928 |
+
|
| 929 |
+
'@floating-ui/[email protected]':
|
| 930 |
+
resolution: {integrity: sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==}
|
| 931 |
+
peerDependencies:
|
| 932 |
+
react: '>=16.8.0'
|
| 933 |
+
react-dom: '>=16.8.0'
|
| 934 |
+
|
| 935 |
+
'@floating-ui/[email protected]':
|
| 936 |
+
resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==}
|
| 937 |
+
peerDependencies:
|
| 938 |
+
react: '>=16.8.0'
|
| 939 |
+
react-dom: '>=16.8.0'
|
| 940 |
+
|
| 941 |
+
'@floating-ui/[email protected]':
|
| 942 |
+
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
| 943 |
+
|
| 944 |
+
'@headlessui/[email protected]':
|
| 945 |
+
resolution: {integrity: sha512-lz+OGcAH1dK93rgSMzXmm1qKOJkBUqZf1L4M8TWLNplftQD3IkoEDdUFNfAn4ylsN6WOTVtWaLmvmaHOUk1dTA==}
|
| 946 |
+
engines: {node: '>=10'}
|
| 947 |
+
peerDependencies:
|
| 948 |
+
react: ^18 || ^19 || ^19.0.0-rc
|
| 949 |
+
react-dom: ^18 || ^19 || ^19.0.0-rc
|
| 950 |
+
|
| 951 |
'@huggingface/[email protected]':
|
| 952 |
resolution: {integrity: sha512-3WXbMFaPkk03LRCM0z0sylmn8ddDm4ubjU7X+Hg4M2GOuMklwoGAFXp9V2keq7vltoB/c7McE5aHUVVddAewsw==}
|
| 953 |
engines: {node: '>=18'}
|
|
|
|
| 1264 |
'@protobufjs/[email protected]':
|
| 1265 |
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
| 1266 |
|
| 1267 |
+
'@react-aria/[email protected]':
|
| 1268 |
+
resolution: {integrity: sha512-JpFtXmWQ0Oca7FcvkqgjSyo6xEP7v3oQOLUId6o0xTvm4AD5W0mU2r3lYrbhsJ+XxdUUX4AVR5473sZZ85kU4A==}
|
| 1269 |
+
peerDependencies:
|
| 1270 |
+
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1271 |
+
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1272 |
+
|
| 1273 |
+
'@react-aria/[email protected]':
|
| 1274 |
+
resolution: {integrity: sha512-J1bhlrNtjPS/fe5uJQ+0c7/jiXniwa4RQlP+Emjfc/iuqpW2RhbF9ou5vROcLzWIyaW8tVMZ468J68rAs/aZ5A==}
|
| 1275 |
+
peerDependencies:
|
| 1276 |
+
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1277 |
+
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1278 |
+
|
| 1279 |
+
'@react-aria/[email protected]':
|
| 1280 |
+
resolution: {integrity: sha512-2P5thfjfPy/np18e5wD4WPt8ydNXhij1jwA8oehxZTFqlgVMGXzcWKxTb4RtJrLFsqPO7RUQTiY8QJk0M4Vy2g==}
|
| 1281 |
+
engines: {node: '>= 12'}
|
| 1282 |
+
peerDependencies:
|
| 1283 |
+
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1284 |
+
|
| 1285 |
+
'@react-aria/[email protected]':
|
| 1286 |
+
resolution: {integrity: sha512-yXMFVJ73rbQ/yYE/49n5Uidjw7kh192WNN9PNQGV0Xoc7EJUlSOxqhnpHmYTyO0EotJ8fdM1fMH8durHjUSI8g==}
|
| 1287 |
+
peerDependencies:
|
| 1288 |
+
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1289 |
+
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1290 |
+
|
| 1291 |
+
'@react-stately/[email protected]':
|
| 1292 |
+
resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==}
|
| 1293 |
+
|
| 1294 |
+
'@react-stately/[email protected]':
|
| 1295 |
+
resolution: {integrity: sha512-cWvjGAocvy4abO9zbr6PW6taHgF24Mwy/LbQ4TC4Aq3tKdKDntxyD+sh7AkSRfJRT2ccMVaHVv2+FfHThd3PKQ==}
|
| 1296 |
+
peerDependencies:
|
| 1297 |
+
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1298 |
+
|
| 1299 |
+
'@react-types/[email protected]':
|
| 1300 |
+
resolution: {integrity: sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==}
|
| 1301 |
+
peerDependencies:
|
| 1302 |
+
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
| 1303 |
+
|
| 1304 |
'@rollup/[email protected]':
|
| 1305 |
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
|
| 1306 |
engines: {node: '>= 10.0.0'}
|
|
|
|
| 1403 |
resolution: {integrity: sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==}
|
| 1404 |
engines: {node: '>=10'}
|
| 1405 |
|
| 1406 |
+
'@swc/[email protected]':
|
| 1407 |
+
resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
|
| 1408 |
+
|
| 1409 |
+
'@tanstack/[email protected]':
|
| 1410 |
+
resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==}
|
| 1411 |
+
peerDependencies:
|
| 1412 |
+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
| 1413 |
+
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
| 1414 |
+
|
| 1415 |
+
'@tanstack/[email protected]':
|
| 1416 |
+
resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==}
|
| 1417 |
+
|
| 1418 |
'@testing-library/[email protected]':
|
| 1419 |
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
|
| 1420 |
engines: {node: '>=18'}
|
|
|
|
| 2156 | |
| 2157 |
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
| 2158 |
|
| 2159 | |
| 2160 |
+
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
| 2161 |
+
engines: {node: '>=6'}
|
| 2162 |
+
|
| 2163 | |
| 2164 |
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
| 2165 |
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
|
|
|
| 5461 | |
| 5462 |
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
| 5463 |
|
| 5464 | |
| 5465 |
+
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
| 5466 |
+
|
| 5467 | |
| 5468 |
resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
|
| 5469 |
engines: {node: '>=14.0.0'}
|
|
|
|
| 5706 | |
| 5707 |
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
| 5708 |
|
| 5709 | |
| 5710 |
+
resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==}
|
| 5711 |
+
peerDependencies:
|
| 5712 |
+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
| 5713 |
+
|
| 5714 | |
| 5715 |
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
| 5716 |
|
|
|
|
| 7043 |
|
| 7044 |
'@eslint/[email protected]': {}
|
| 7045 |
|
| 7046 |
+
'@floating-ui/[email protected]':
|
| 7047 |
+
dependencies:
|
| 7048 |
+
'@floating-ui/utils': 0.2.10
|
| 7049 |
+
|
| 7050 |
+
'@floating-ui/[email protected]':
|
| 7051 |
+
dependencies:
|
| 7052 |
+
'@floating-ui/core': 1.7.2
|
| 7053 |
+
'@floating-ui/utils': 0.2.10
|
| 7054 |
+
|
| 7055 |
+
'@floating-ui/[email protected]([email protected]([email protected]))([email protected])':
|
| 7056 |
+
dependencies:
|
| 7057 |
+
'@floating-ui/dom': 1.7.2
|
| 7058 |
+
react: 19.1.0
|
| 7059 |
+
react-dom: 19.1.0([email protected])
|
| 7060 |
+
|
| 7061 |
+
'@floating-ui/[email protected]([email protected]([email protected]))([email protected])':
|
| 7062 |
+
dependencies:
|
| 7063 |
+
'@floating-ui/react-dom': 2.1.4([email protected]([email protected]))([email protected])
|
| 7064 |
+
'@floating-ui/utils': 0.2.10
|
| 7065 |
+
react: 19.1.0
|
| 7066 |
+
react-dom: 19.1.0([email protected])
|
| 7067 |
+
tabbable: 6.2.0
|
| 7068 |
+
|
| 7069 |
+
'@floating-ui/[email protected]': {}
|
| 7070 |
+
|
| 7071 |
+
'@headlessui/[email protected]([email protected]([email protected]))([email protected])':
|
| 7072 |
+
dependencies:
|
| 7073 |
+
'@floating-ui/react': 0.26.28([email protected]([email protected]))([email protected])
|
| 7074 |
+
'@react-aria/focus': 3.20.5([email protected]([email protected]))([email protected])
|
| 7075 |
+
'@react-aria/interactions': 3.25.3([email protected]([email protected]))([email protected])
|
| 7076 |
+
'@tanstack/react-virtual': 3.13.12([email protected]([email protected]))([email protected])
|
| 7077 |
+
react: 19.1.0
|
| 7078 |
+
react-dom: 19.1.0([email protected])
|
| 7079 |
+
use-sync-external-store: 1.5.0([email protected])
|
| 7080 |
+
|
| 7081 |
'@huggingface/[email protected]': {}
|
| 7082 |
|
| 7083 |
'@huggingface/[email protected]':
|
|
|
|
| 7458 |
|
| 7459 |
'@protobufjs/[email protected]': {}
|
| 7460 |
|
| 7461 |
+
'@react-aria/[email protected]([email protected]([email protected]))([email protected])':
|
| 7462 |
+
dependencies:
|
| 7463 |
+
'@react-aria/interactions': 3.25.3([email protected]([email protected]))([email protected])
|
| 7464 |
+
'@react-aria/utils': 3.29.1([email protected]([email protected]))([email protected])
|
| 7465 |
+
'@react-types/shared': 3.30.0([email protected])
|
| 7466 |
+
'@swc/helpers': 0.5.17
|
| 7467 |
+
clsx: 2.1.1
|
| 7468 |
+
react: 19.1.0
|
| 7469 |
+
react-dom: 19.1.0([email protected])
|
| 7470 |
+
|
| 7471 |
+
'@react-aria/[email protected]([email protected]([email protected]))([email protected])':
|
| 7472 |
+
dependencies:
|
| 7473 |
+
'@react-aria/ssr': 3.9.9([email protected])
|
| 7474 |
+
'@react-aria/utils': 3.29.1([email protected]([email protected]))([email protected])
|
| 7475 |
+
'@react-stately/flags': 3.1.2
|
| 7476 |
+
'@react-types/shared': 3.30.0([email protected])
|
| 7477 |
+
'@swc/helpers': 0.5.17
|
| 7478 |
+
react: 19.1.0
|
| 7479 |
+
react-dom: 19.1.0([email protected])
|
| 7480 |
+
|
| 7481 |
+
'@react-aria/[email protected]([email protected])':
|
| 7482 |
+
dependencies:
|
| 7483 |
+
'@swc/helpers': 0.5.17
|
| 7484 |
+
react: 19.1.0
|
| 7485 |
+
|
| 7486 |
+
'@react-aria/[email protected]([email protected]([email protected]))([email protected])':
|
| 7487 |
+
dependencies:
|
| 7488 |
+
'@react-aria/ssr': 3.9.9([email protected])
|
| 7489 |
+
'@react-stately/flags': 3.1.2
|
| 7490 |
+
'@react-stately/utils': 3.10.7([email protected])
|
| 7491 |
+
'@react-types/shared': 3.30.0([email protected])
|
| 7492 |
+
'@swc/helpers': 0.5.17
|
| 7493 |
+
clsx: 2.1.1
|
| 7494 |
+
react: 19.1.0
|
| 7495 |
+
react-dom: 19.1.0([email protected])
|
| 7496 |
+
|
| 7497 |
+
'@react-stately/[email protected]':
|
| 7498 |
+
dependencies:
|
| 7499 |
+
'@swc/helpers': 0.5.17
|
| 7500 |
+
|
| 7501 |
+
'@react-stately/[email protected]([email protected])':
|
| 7502 |
+
dependencies:
|
| 7503 |
+
'@swc/helpers': 0.5.17
|
| 7504 |
+
react: 19.1.0
|
| 7505 |
+
|
| 7506 |
+
'@react-types/[email protected]([email protected])':
|
| 7507 |
+
dependencies:
|
| 7508 |
+
react: 19.1.0
|
| 7509 |
+
|
| 7510 |
'@rollup/[email protected](@babel/[email protected])(@types/[email protected])([email protected])':
|
| 7511 |
dependencies:
|
| 7512 |
'@babel/core': 7.28.0
|
|
|
|
| 7629 |
transitivePeerDependencies:
|
| 7630 |
- supports-color
|
| 7631 |
|
| 7632 |
+
'@swc/[email protected]':
|
| 7633 |
+
dependencies:
|
| 7634 |
+
tslib: 2.8.1
|
| 7635 |
+
|
| 7636 |
+
'@tanstack/[email protected]([email protected]([email protected]))([email protected])':
|
| 7637 |
+
dependencies:
|
| 7638 |
+
'@tanstack/virtual-core': 3.13.12
|
| 7639 |
+
react: 19.1.0
|
| 7640 |
+
react-dom: 19.1.0([email protected])
|
| 7641 |
+
|
| 7642 |
+
'@tanstack/[email protected]': {}
|
| 7643 |
+
|
| 7644 |
'@testing-library/[email protected]':
|
| 7645 |
dependencies:
|
| 7646 |
'@babel/code-frame': 7.27.1
|
|
|
|
| 8564 |
strip-ansi: 6.0.1
|
| 8565 |
wrap-ansi: 7.0.0
|
| 8566 |
|
| 8567 |
+
[email protected]: {}
|
| 8568 |
+
|
| 8569 | |
| 8570 |
|
| 8571 | |
|
|
|
| 12477 |
|
| 12478 | |
| 12479 |
|
| 12480 |
+
[email protected]: {}
|
| 12481 |
+
|
| 12482 | |
| 12483 |
dependencies:
|
| 12484 |
'@alloc/quick-lru': 5.2.0
|
|
|
|
| 12731 |
querystringify: 2.2.0
|
| 12732 |
requires-port: 1.0.0
|
| 12733 |
|
| 12734 | |
| 12735 |
+
dependencies:
|
| 12736 |
+
react: 19.1.0
|
| 12737 |
+
|
| 12738 | |
| 12739 |
|
| 12740 |
src/App.tsx
CHANGED
|
@@ -5,31 +5,22 @@ import TextClassification from './components/TextClassification'
|
|
| 5 |
import Header from './Header'
|
| 6 |
import Footer from './Footer'
|
| 7 |
import { useModel } from './contexts/ModelContext'
|
| 8 |
-
import {
|
| 9 |
-
import { getModelsByPipeline, getModelSize } from './lib/huggingface'
|
| 10 |
import ModelSelector from './components/ModelSelector'
|
|
|
|
| 11 |
|
| 12 |
function App() {
|
| 13 |
-
const { pipeline, setPipeline, progress, status, modelInfo, setModels } =
|
|
|
|
| 14 |
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
} else if (num >= 1000) {
|
| 21 |
-
return (num / 1000).toFixed(1) + 'K'
|
| 22 |
}
|
| 23 |
-
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
useEffect(() => {
|
| 27 |
-
const fetchModels = async () => {
|
| 28 |
-
const fetchedModels = await getModelsByPipeline(pipeline);
|
| 29 |
-
setModels(fetchedModels);
|
| 30 |
-
};
|
| 31 |
-
fetchModels();
|
| 32 |
-
}, [setModels, pipeline]);
|
| 33 |
|
| 34 |
return (
|
| 35 |
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
|
@@ -40,66 +31,27 @@ function App() {
|
|
| 40 |
<div className="mb-8">
|
| 41 |
<div className="bg-white rounded-lg shadow-sm border p-6">
|
| 42 |
<div className="flex items-center justify-between mb-4">
|
| 43 |
-
|
| 44 |
-
{modelInfo.name && (
|
| 45 |
-
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 px-4 py-3 rounded-lg border border-blue-200 space-y-2">
|
| 46 |
-
{/* Model Name Row */}
|
| 47 |
-
<div className="flex items-center space-x-2">
|
| 48 |
-
<Bot className="w-4 h-4 text-blue-600" />
|
| 49 |
-
<span
|
| 50 |
-
className="text-sm font-medium text-gray-700 truncate max-w-100"
|
| 51 |
-
title={modelInfo.name}
|
| 52 |
-
>
|
| 53 |
-
{modelInfo.name.split('/').pop()}
|
| 54 |
-
</span>
|
| 55 |
-
</div>
|
| 56 |
-
|
| 57 |
-
{/* Stats Row */}
|
| 58 |
-
<div className="flex items-center justify-self-end space-x-4 text-xs text-gray-600">
|
| 59 |
-
{modelInfo.likes > 0 && (
|
| 60 |
-
<div className="flex items-center space-x-1">
|
| 61 |
-
<Heart className="w-3 h-3 text-red-500" />
|
| 62 |
-
<span>{formatNumber(modelInfo.likes)}</span>
|
| 63 |
-
</div>
|
| 64 |
-
)}
|
| 65 |
-
|
| 66 |
-
{modelInfo.downloads > 0 && (
|
| 67 |
-
<div className="flex items-center space-x-1">
|
| 68 |
-
<Download className="w-3 h-3 text-green-500" />
|
| 69 |
-
<span>{formatNumber(modelInfo.downloads)}</span>
|
| 70 |
-
</div>
|
| 71 |
-
)}
|
| 72 |
-
|
| 73 |
-
{modelInfo.parameters > 0 && (
|
| 74 |
-
<div className="flex items-center space-x-1">
|
| 75 |
-
<Cpu className="w-3 h-3 text-purple-500" />
|
| 76 |
-
<span>{formatNumber(modelInfo.parameters)}</span>
|
| 77 |
-
</div>
|
| 78 |
-
)}
|
| 79 |
-
|
| 80 |
-
{modelInfo.parameters > 0 && (
|
| 81 |
-
<div className="flex items-center space-x-1">
|
| 82 |
-
<DatabaseIcon className="w-3 h-3 text-purple-500" />
|
| 83 |
-
<span>
|
| 84 |
-
{`~${getModelSize(
|
| 85 |
-
modelInfo.parameters,
|
| 86 |
-
'INT8'
|
| 87 |
-
).toFixed(1)}MB`}
|
| 88 |
-
</span>
|
| 89 |
-
</div>
|
| 90 |
-
)}
|
| 91 |
-
</div>
|
| 92 |
-
</div>
|
| 93 |
-
)}
|
| 94 |
</div>
|
| 95 |
|
| 96 |
-
<div className="
|
| 97 |
-
<
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
</div>
|
| 102 |
-
<ModelSelector />
|
| 103 |
|
| 104 |
{/* Model Loading Progress */}
|
| 105 |
{status === 'progress' && (
|
|
|
|
| 5 |
import Header from './Header'
|
| 6 |
import Footer from './Footer'
|
| 7 |
import { useModel } from './contexts/ModelContext'
|
| 8 |
+
import { getModelsByPipeline } from './lib/huggingface'
|
|
|
|
| 9 |
import ModelSelector from './components/ModelSelector'
|
| 10 |
+
import ModelInfo from './components/ModelInfo'
|
| 11 |
|
| 12 |
function App() {
|
| 13 |
+
const { pipeline, setPipeline, progress, status, modelInfo, setModels } =
|
| 14 |
+
useModel()
|
| 15 |
|
| 16 |
+
useEffect(() => {
|
| 17 |
+
const fetchModels = async () => {
|
| 18 |
+
const fetchedModels = await getModelsByPipeline(pipeline)
|
| 19 |
+
setModels(fetchedModels)
|
| 20 |
+
console.log(fetchedModels)
|
|
|
|
|
|
|
| 21 |
}
|
| 22 |
+
fetchModels()
|
| 23 |
+
}, [setModels, pipeline])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
return (
|
| 26 |
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
|
|
|
| 31 |
<div className="mb-8">
|
| 32 |
<div className="bg-white rounded-lg shadow-sm border p-6">
|
| 33 |
<div className="flex items-center justify-between mb-4">
|
| 34 |
+
<ModelInfo />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
</div>
|
| 36 |
|
| 37 |
+
<div className="grid grid-cols-1 xl:grid-cols-2 gap-4 items-start">
|
| 38 |
+
<div className="space-y-2">
|
| 39 |
+
<span className="text-lg font-semibold text-gray-900 block">
|
| 40 |
+
Choose a Pipeline
|
| 41 |
+
</span>
|
| 42 |
+
<PipelineSelector
|
| 43 |
+
pipeline={pipeline}
|
| 44 |
+
setPipeline={setPipeline}
|
| 45 |
+
/>
|
| 46 |
+
</div>
|
| 47 |
+
|
| 48 |
+
<div className="space-y-2">
|
| 49 |
+
<span className="text-lg font-semibold text-gray-900 block">
|
| 50 |
+
Select Model
|
| 51 |
+
</span>
|
| 52 |
+
<ModelSelector />
|
| 53 |
+
</div>
|
| 54 |
</div>
|
|
|
|
| 55 |
|
| 56 |
{/* Model Loading Progress */}
|
| 57 |
{status === 'progress' && (
|
src/components/ModelInfo.tsx
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Bot, Heart, Download, Cpu, DatabaseIcon, CheckCircle, XCircle, ExternalLink, ChevronDown } from 'lucide-react'
|
| 2 |
+
import { getModelSize } from '../lib/huggingface'
|
| 3 |
+
import { useModel } from '../contexts/ModelContext'
|
| 4 |
+
import { useEffect } from 'react'
|
| 5 |
+
import { QuantizationType } from '../types'
|
| 6 |
+
|
| 7 |
+
const ModelInfo = () => {
|
| 8 |
+
const formatNumber = (num: number) => {
|
| 9 |
+
if (num >= 1000000000) {
|
| 10 |
+
return (num / 1000000000).toFixed(1) + 'B'
|
| 11 |
+
} else if (num >= 1000000) {
|
| 12 |
+
return (num / 1000000).toFixed(1) + 'M'
|
| 13 |
+
} else if (num >= 1000) {
|
| 14 |
+
return (num / 1000).toFixed(1) + 'K'
|
| 15 |
+
}
|
| 16 |
+
return num.toString()
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
const { modelInfo, selectedQuantization, setSelectedQuantization } = useModel()
|
| 20 |
+
|
| 21 |
+
// Set default quantization when model changes
|
| 22 |
+
useEffect(() => {
|
| 23 |
+
if (modelInfo.isCompatible && modelInfo.supportedQuantizations.length > 0) {
|
| 24 |
+
const quantizations = modelInfo.supportedQuantizations
|
| 25 |
+
let defaultQuant: QuantizationType = 'fp32'
|
| 26 |
+
|
| 27 |
+
if (quantizations.includes('int8')) {
|
| 28 |
+
defaultQuant = 'int8'
|
| 29 |
+
} else if (quantizations.includes('q8')) {
|
| 30 |
+
defaultQuant = 'q8'
|
| 31 |
+
} else if (quantizations.includes('q4')) {
|
| 32 |
+
defaultQuant = 'q4'
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
setSelectedQuantization(defaultQuant)
|
| 36 |
+
}
|
| 37 |
+
}, [modelInfo.supportedQuantizations, modelInfo.isCompatible, setSelectedQuantization])
|
| 38 |
+
|
| 39 |
+
if (!modelInfo.name) {
|
| 40 |
+
return null
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
return (
|
| 44 |
+
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 px-4 py-3 rounded-lg border border-blue-200 space-y-3">
|
| 45 |
+
{/* Model Name Row */}
|
| 46 |
+
<div className="flex items-center space-x-2">
|
| 47 |
+
<Bot className="w-4 h-4 text-blue-600" />
|
| 48 |
+
<a
|
| 49 |
+
href={`https://huggingface.co/${modelInfo.name}`}
|
| 50 |
+
target="_blank"
|
| 51 |
+
rel="noopener noreferrer"
|
| 52 |
+
className="text-sm font-medium text-gray-700 truncate max-w-100 hover:underline"
|
| 53 |
+
title={modelInfo.name}
|
| 54 |
+
>
|
| 55 |
+
<ExternalLink className="w-3 h-3 inline-block mr-1" />
|
| 56 |
+
{modelInfo.name}
|
| 57 |
+
</a>
|
| 58 |
+
{/* Compatibility Status */}
|
| 59 |
+
{typeof modelInfo.isCompatible === 'boolean' && (
|
| 60 |
+
<div className="flex items-center space-x-1">
|
| 61 |
+
{modelInfo.isCompatible ? (
|
| 62 |
+
<>
|
| 63 |
+
<CheckCircle className="w-4 h-4 text-green-500" />
|
| 64 |
+
</>
|
| 65 |
+
) : (
|
| 66 |
+
<>
|
| 67 |
+
<XCircle className="w-4 h-4 text-red-500" />
|
| 68 |
+
</>
|
| 69 |
+
)}
|
| 70 |
+
</div>
|
| 71 |
+
)}
|
| 72 |
+
</div>
|
| 73 |
+
|
| 74 |
+
{/* Base Model Link */}
|
| 75 |
+
{modelInfo.baseId && (
|
| 76 |
+
<div className="flex items-center space-x-2 ml-6">
|
| 77 |
+
<a
|
| 78 |
+
href={`https://huggingface.co/${modelInfo.baseId}`}
|
| 79 |
+
target="_blank"
|
| 80 |
+
rel="noopener noreferrer"
|
| 81 |
+
className="text-xs text-gray-600 truncate max-w-100 hover:underline"
|
| 82 |
+
title={`Base model: ${modelInfo.baseId}`}
|
| 83 |
+
>
|
| 84 |
+
<ExternalLink className="w-3 h-3 inline-block mr-1" />
|
| 85 |
+
{modelInfo.baseId}
|
| 86 |
+
</a>
|
| 87 |
+
</div>
|
| 88 |
+
)}
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
{/* Stats Row */}
|
| 92 |
+
<div className="flex items-center justify-self-end space-x-4 text-xs text-gray-600">
|
| 93 |
+
{modelInfo.likes > 0 && (
|
| 94 |
+
<div className="flex items-center space-x-1">
|
| 95 |
+
<Heart className="w-3 h-3 text-red-500" />
|
| 96 |
+
<span>{formatNumber(modelInfo.likes)}</span>
|
| 97 |
+
</div>
|
| 98 |
+
)}
|
| 99 |
+
|
| 100 |
+
{modelInfo.downloads > 0 && (
|
| 101 |
+
<div className="flex items-center space-x-1">
|
| 102 |
+
<Download className="w-3 h-3 text-green-500" />
|
| 103 |
+
<span>{formatNumber(modelInfo.downloads)}</span>
|
| 104 |
+
</div>
|
| 105 |
+
)}
|
| 106 |
+
|
| 107 |
+
{modelInfo.parameters > 0 && (
|
| 108 |
+
<div className="flex items-center space-x-1">
|
| 109 |
+
<Cpu className="w-3 h-3 text-purple-500" />
|
| 110 |
+
<span>{formatNumber(modelInfo.parameters)}</span>
|
| 111 |
+
</div>
|
| 112 |
+
)}
|
| 113 |
+
|
| 114 |
+
{modelInfo.parameters > 0 && (
|
| 115 |
+
<div className="flex items-center space-x-1">
|
| 116 |
+
<DatabaseIcon className="w-3 h-3 text-purple-500" />
|
| 117 |
+
<span>
|
| 118 |
+
{`~${getModelSize(modelInfo.parameters, selectedQuantization).toFixed(1)}MB`}
|
| 119 |
+
</span>
|
| 120 |
+
</div>
|
| 121 |
+
)}
|
| 122 |
+
</div>
|
| 123 |
+
|
| 124 |
+
{/* Separator */}
|
| 125 |
+
{modelInfo.isCompatible && modelInfo.supportedQuantizations.length > 0 && (
|
| 126 |
+
<hr className="border-gray-200" />
|
| 127 |
+
)}
|
| 128 |
+
|
| 129 |
+
{/* Quantization Dropdown */}
|
| 130 |
+
{modelInfo.isCompatible && modelInfo.supportedQuantizations.length > 0 && (
|
| 131 |
+
<div className="flex items-center space-x-2">
|
| 132 |
+
<span className="text-xs text-gray-600 font-medium">Quantization:</span>
|
| 133 |
+
<div className="relative">
|
| 134 |
+
<select
|
| 135 |
+
value={selectedQuantization || ''}
|
| 136 |
+
onChange={(e) => setSelectedQuantization(e.target.value as QuantizationType)}
|
| 137 |
+
className="appearance-none bg-white border border-gray-300 rounded-md px-3 py-1 pr-8 text-xs text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
| 138 |
+
>
|
| 139 |
+
<option value="">Select quantization</option>
|
| 140 |
+
{modelInfo.supportedQuantizations.map((quant) => (
|
| 141 |
+
<option key={quant} value={quant}>
|
| 142 |
+
{quant}
|
| 143 |
+
</option>
|
| 144 |
+
))}
|
| 145 |
+
</select>
|
| 146 |
+
<ChevronDown className="absolute right-2 top-1/2 transform -translate-y-1/2 w-3 h-3 text-gray-400 pointer-events-none" />
|
| 147 |
+
</div>
|
| 148 |
+
</div>
|
| 149 |
+
)}
|
| 150 |
+
|
| 151 |
+
{/* Incompatibility Message */}
|
| 152 |
+
{modelInfo.isCompatible === false && modelInfo.incompatibilityReason && (
|
| 153 |
+
<div className="bg-red-50 border border-red-200 rounded-md px-3 py-2">
|
| 154 |
+
<p className="text-sm text-red-700">
|
| 155 |
+
<span className="font-medium">Incompatible:</span>{' '}
|
| 156 |
+
{modelInfo.incompatibilityReason}
|
| 157 |
+
</p>
|
| 158 |
+
</div>
|
| 159 |
+
)}
|
| 160 |
+
</div>
|
| 161 |
+
)
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
export default ModelInfo
|
src/components/ModelSelector.tsx
CHANGED
|
@@ -1,14 +1,15 @@
|
|
| 1 |
import React, { useEffect, useState } from 'react'
|
|
|
|
| 2 |
import { useModel } from '../contexts/ModelContext'
|
| 3 |
import { getModelInfo } from '../lib/huggingface'
|
| 4 |
-
import { Heart, Download, ChevronDown } from 'lucide-react'
|
|
|
|
|
|
|
| 5 |
|
| 6 |
const ModelSelector: React.FC = () => {
|
| 7 |
-
const { models, setModelInfo, modelInfo } = useModel()
|
| 8 |
-
const [
|
| 9 |
-
const [
|
| 10 |
-
Record<string, { likes: number; downloads: number; createdAt: string }>
|
| 11 |
-
>({})
|
| 12 |
|
| 13 |
const formatNumber = (num: number) => {
|
| 14 |
if (num >= 1000000000) {
|
|
@@ -21,39 +22,49 @@ const ModelSelector: React.FC = () => {
|
|
| 21 |
return num.toString()
|
| 22 |
}
|
| 23 |
|
| 24 |
-
//
|
| 25 |
-
const
|
| 26 |
-
|
| 27 |
-
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
try {
|
| 45 |
const modelInfoResponse = await getModelInfo(modelId)
|
|
|
|
| 46 |
let parameters = 0
|
| 47 |
if (modelInfoResponse.safetensors) {
|
| 48 |
const safetensors = modelInfoResponse.safetensors
|
| 49 |
parameters =
|
|
|
|
| 50 |
safetensors.parameters.F16 ||
|
| 51 |
safetensors.parameters.F32 ||
|
| 52 |
safetensors.parameters.total ||
|
| 53 |
0
|
| 54 |
}
|
| 55 |
|
| 56 |
-
// Transform ModelInfoResponse to ModelInfo
|
| 57 |
const modelInfo = {
|
| 58 |
id: modelId,
|
| 59 |
name: modelInfoResponse.id || modelId,
|
|
@@ -61,20 +72,14 @@ const ModelSelector: React.FC = () => {
|
|
| 61 |
parameters,
|
| 62 |
likes: modelInfoResponse.likes || 0,
|
| 63 |
downloads: modelInfoResponse.downloads || 0,
|
| 64 |
-
createdAt: modelInfoResponse.createdAt || ''
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
}
|
| 66 |
|
| 67 |
-
|
| 68 |
-
setModelStats((prev) => ({
|
| 69 |
-
...prev,
|
| 70 |
-
[modelId]: {
|
| 71 |
-
likes: modelInfoResponse.likes || 0,
|
| 72 |
-
downloads: modelInfoResponse.downloads || 0,
|
| 73 |
-
createdAt: modelInfoResponse.createdAt || ''
|
| 74 |
-
}
|
| 75 |
-
}))
|
| 76 |
-
|
| 77 |
-
console.log(modelInfoResponse)
|
| 78 |
|
| 79 |
setModelInfo(modelInfo)
|
| 80 |
} catch (error) {
|
|
@@ -82,97 +87,175 @@ const ModelSelector: React.FC = () => {
|
|
| 82 |
}
|
| 83 |
}
|
| 84 |
|
| 85 |
-
//
|
| 86 |
useEffect(() => {
|
| 87 |
-
models.
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
}
|
| 91 |
-
})
|
| 92 |
-
}, [models])
|
| 93 |
-
|
| 94 |
-
// Only fetch full info when a model is actually selected
|
| 95 |
-
useEffect(() => {
|
| 96 |
-
if (!modelInfo.id) return
|
| 97 |
-
// Only fetch if we don't already have the full info
|
| 98 |
-
if (!modelStats[modelInfo.id]) {
|
| 99 |
-
fetchModelAndSetInfo(modelInfo.id)
|
| 100 |
}
|
| 101 |
-
}, [
|
| 102 |
|
| 103 |
const handleModelSelect = (modelId: string) => {
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
}
|
| 107 |
|
|
|
|
|
|
|
| 108 |
return (
|
| 109 |
<div className="relative">
|
| 110 |
-
{
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
(modelStats[model.id].likes > 0 ||
|
| 140 |
-
modelStats[model.id].downloads > 0) && (
|
| 141 |
-
<div className="flex items-center space-x-3 text-xs text-gray-500 flex-shrink-0">
|
| 142 |
-
{modelStats[model.id].likes > 0 && (
|
| 143 |
-
<div className="flex items-center space-x-1">
|
| 144 |
-
<Heart className="w-3 h-3 text-red-500" />
|
| 145 |
-
<span>
|
| 146 |
-
{formatNumber(modelStats[model.id].likes)}
|
| 147 |
-
</span>
|
| 148 |
-
</div>
|
| 149 |
-
)}
|
| 150 |
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
</div>
|
| 158 |
)}
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
</span>
|
| 163 |
-
)}
|
| 164 |
-
</div>
|
| 165 |
-
)}
|
| 166 |
</div>
|
| 167 |
-
</
|
| 168 |
-
|
| 169 |
</div>
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
{/* Click outside to close */}
|
| 173 |
-
{isOpen && (
|
| 174 |
-
<div className="fixed inset-0 z-0" onClick={() => setIsOpen(false)} />
|
| 175 |
-
)}
|
| 176 |
</div>
|
| 177 |
)
|
| 178 |
}
|
|
|
|
| 1 |
import React, { useEffect, useState } from 'react'
|
| 2 |
+
import { Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from '@headlessui/react'
|
| 3 |
import { useModel } from '../contexts/ModelContext'
|
| 4 |
import { getModelInfo } from '../lib/huggingface'
|
| 5 |
+
import { Heart, Download, ChevronDown, Check, ArrowUpDown } from 'lucide-react'
|
| 6 |
+
|
| 7 |
+
type SortOption = 'likes' | 'downloads' | 'createdAt' | 'name'
|
| 8 |
|
| 9 |
const ModelSelector: React.FC = () => {
|
| 10 |
+
const { models, setModelInfo, modelInfo, pipeline } = useModel()
|
| 11 |
+
const [sortBy, setSortBy] = useState<SortOption>('likes')
|
| 12 |
+
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc')
|
|
|
|
|
|
|
| 13 |
|
| 14 |
const formatNumber = (num: number) => {
|
| 15 |
if (num >= 1000000000) {
|
|
|
|
| 22 |
return num.toString()
|
| 23 |
}
|
| 24 |
|
| 25 |
+
// Sort models based on current sort criteria
|
| 26 |
+
const sortedModels = React.useMemo(() => {
|
| 27 |
+
return [...models].sort((a, b) => {
|
| 28 |
+
let comparison = 0
|
| 29 |
|
| 30 |
+
switch (sortBy) {
|
| 31 |
+
case 'downloads':
|
| 32 |
+
comparison = (a.downloads || 0) - (b.downloads || 0)
|
| 33 |
+
break
|
| 34 |
+
case 'createdAt':
|
| 35 |
+
const dateA = new Date(a.createdAt || '').getTime()
|
| 36 |
+
const dateB = new Date(b.createdAt || '').getTime()
|
| 37 |
+
comparison = dateA - dateB
|
| 38 |
+
break
|
| 39 |
+
case 'name':
|
| 40 |
+
comparison = a.id.localeCompare(b.id)
|
| 41 |
+
break
|
| 42 |
+
case 'likes':
|
| 43 |
+
default:
|
| 44 |
+
comparison = (a.likes || 0) - (b.likes || 0)
|
| 45 |
+
break
|
| 46 |
+
}
|
| 47 |
|
| 48 |
+
return sortOrder === 'desc' ? -comparison : comparison
|
| 49 |
+
})
|
| 50 |
+
}, [models, sortBy, sortOrder])
|
| 51 |
+
|
| 52 |
+
// Function to fetch detailed model info and set as selected
|
| 53 |
+
const fetchAndSetModelInfo = async (modelId: string) => {
|
| 54 |
try {
|
| 55 |
const modelInfoResponse = await getModelInfo(modelId)
|
| 56 |
+
|
| 57 |
let parameters = 0
|
| 58 |
if (modelInfoResponse.safetensors) {
|
| 59 |
const safetensors = modelInfoResponse.safetensors
|
| 60 |
parameters =
|
| 61 |
+
safetensors.parameters.BF16 ||
|
| 62 |
safetensors.parameters.F16 ||
|
| 63 |
safetensors.parameters.F32 ||
|
| 64 |
safetensors.parameters.total ||
|
| 65 |
0
|
| 66 |
}
|
| 67 |
|
|
|
|
| 68 |
const modelInfo = {
|
| 69 |
id: modelId,
|
| 70 |
name: modelInfoResponse.id || modelId,
|
|
|
|
| 72 |
parameters,
|
| 73 |
likes: modelInfoResponse.likes || 0,
|
| 74 |
downloads: modelInfoResponse.downloads || 0,
|
| 75 |
+
createdAt: modelInfoResponse.createdAt || '',
|
| 76 |
+
isCompatible: modelInfoResponse.isCompatible,
|
| 77 |
+
incompatibilityReason: modelInfoResponse.incompatibilityReason,
|
| 78 |
+
supportedQuantizations: modelInfoResponse.supportedQuantizations,
|
| 79 |
+
baseId: modelInfoResponse.baseId
|
| 80 |
}
|
| 81 |
|
| 82 |
+
console.log('Fetched model info:', modelInfoResponse)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
setModelInfo(modelInfo)
|
| 85 |
} catch (error) {
|
|
|
|
| 87 |
}
|
| 88 |
}
|
| 89 |
|
| 90 |
+
// Update modelInfo to first model when pipeline changes
|
| 91 |
useEffect(() => {
|
| 92 |
+
if (models.length > 0) {
|
| 93 |
+
const firstModel = models[0]
|
| 94 |
+
fetchAndSetModelInfo(firstModel.id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
}
|
| 96 |
+
}, [pipeline, models])
|
| 97 |
|
| 98 |
const handleModelSelect = (modelId: string) => {
|
| 99 |
+
fetchAndSetModelInfo(modelId)
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
const handleSortChange = (newSortBy: SortOption) => {
|
| 103 |
+
if (sortBy === newSortBy) {
|
| 104 |
+
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')
|
| 105 |
+
} else {
|
| 106 |
+
setSortBy(newSortBy)
|
| 107 |
+
setSortOrder('desc')
|
| 108 |
+
}
|
| 109 |
}
|
| 110 |
|
| 111 |
+
const selectedModel = models.find(model => model.id === modelInfo.id) || models[0]
|
| 112 |
+
|
| 113 |
return (
|
| 114 |
<div className="relative">
|
| 115 |
+
<Listbox value={selectedModel} onChange={(model) => handleModelSelect(model.id)}>
|
| 116 |
+
<div className="relative">
|
| 117 |
+
<ListboxButton className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white text-left flex items-center justify-between">
|
| 118 |
+
<div className="flex items-center justify-between w-full">
|
| 119 |
+
<div className="flex flex-col flex-1 min-w-0">
|
| 120 |
+
<span className="truncate font-medium">{modelInfo.id || 'Select a model'}</span>
|
| 121 |
+
</div>
|
| 122 |
+
|
| 123 |
+
<div className="flex items-center space-x-3">
|
| 124 |
+
{selectedModel && (selectedModel.likes > 0 || selectedModel.downloads > 0) && (
|
| 125 |
+
<div className="flex items-center space-x-3 text-xs text-gray-500">
|
| 126 |
+
{selectedModel.likes > 0 && (
|
| 127 |
+
<div className="flex items-center space-x-1">
|
| 128 |
+
<Heart className="w-3 h-3 text-red-500" />
|
| 129 |
+
<span>{formatNumber(selectedModel.likes)}</span>
|
| 130 |
+
</div>
|
| 131 |
+
)}
|
| 132 |
+
{selectedModel.downloads > 0 && (
|
| 133 |
+
<div className="flex items-center space-x-1">
|
| 134 |
+
<Download className="w-3 h-3 text-green-500" />
|
| 135 |
+
<span>{formatNumber(selectedModel.downloads)}</span>
|
| 136 |
+
</div>
|
| 137 |
+
)}
|
| 138 |
+
</div>
|
| 139 |
+
)}
|
| 140 |
+
<ChevronDown className="w-4 h-4 ui-open:rotate-180 transition-transform flex-shrink-0" />
|
| 141 |
+
</div>
|
| 142 |
+
</div>
|
| 143 |
+
</ListboxButton>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
+
<Transition
|
| 146 |
+
enter="transition duration-100 ease-out"
|
| 147 |
+
enterFrom="transform scale-95 opacity-0"
|
| 148 |
+
enterTo="transform scale-100 opacity-100"
|
| 149 |
+
leave="transition duration-75 ease-out"
|
| 150 |
+
leaveFrom="transform scale-100 opacity-100"
|
| 151 |
+
leaveTo="transform scale-95 opacity-0"
|
| 152 |
+
>
|
| 153 |
+
<ListboxOptions className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-hidden focus:outline-none">
|
| 154 |
+
{/* Sort Controls - Always Visible */}
|
| 155 |
+
<div className="px-3 py-2 border-b border-gray-200 bg-gray-50 sticky top-0 z-10">
|
| 156 |
+
<div className="flex items-center space-x-2 text-xs">
|
| 157 |
+
<span className="text-gray-600 font-medium">Sort by:</span>
|
| 158 |
+
<button
|
| 159 |
+
onClick={() => handleSortChange('name')}
|
| 160 |
+
className={`px-2 py-1 rounded flex items-center space-x-1 ${
|
| 161 |
+
sortBy === 'name' ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-100'
|
| 162 |
+
}`}
|
| 163 |
+
>
|
| 164 |
+
<span>Name</span>
|
| 165 |
+
{sortBy === 'name' && <ArrowUpDown className="w-3 h-3" />}
|
| 166 |
+
</button>
|
| 167 |
+
<button
|
| 168 |
+
onClick={() => handleSortChange('likes')}
|
| 169 |
+
className={`px-2 py-1 rounded flex items-center space-x-1 ${
|
| 170 |
+
sortBy === 'likes' ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-100'
|
| 171 |
+
}`}
|
| 172 |
+
>
|
| 173 |
+
<Heart className="w-3 h-3" />
|
| 174 |
+
<span>Likes</span>
|
| 175 |
+
{sortBy === 'likes' && <ArrowUpDown className="w-3 h-3" />}
|
| 176 |
+
</button>
|
| 177 |
+
<button
|
| 178 |
+
onClick={() => handleSortChange('downloads')}
|
| 179 |
+
className={`px-2 py-1 rounded flex items-center space-x-1 ${
|
| 180 |
+
sortBy === 'downloads' ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-100'
|
| 181 |
+
}`}
|
| 182 |
+
>
|
| 183 |
+
<Download className="w-3 h-3" />
|
| 184 |
+
<span>Downloads</span>
|
| 185 |
+
{sortBy === 'downloads' && <ArrowUpDown className="w-3 h-3" />}
|
| 186 |
+
</button>
|
| 187 |
+
<button
|
| 188 |
+
onClick={() => handleSortChange('createdAt')}
|
| 189 |
+
className={`px-2 py-1 rounded flex items-center space-x-1 ${
|
| 190 |
+
sortBy === 'createdAt' ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-100'
|
| 191 |
+
}`}
|
| 192 |
+
>
|
| 193 |
+
<span>Date</span>
|
| 194 |
+
{sortBy === 'createdAt' && <ArrowUpDown className="w-3 h-3" />}
|
| 195 |
+
</button>
|
| 196 |
+
</div>
|
| 197 |
+
</div>
|
| 198 |
+
|
| 199 |
+
{/* Model Options - Scrollable */}
|
| 200 |
+
<div className="overflow-auto max-h-48">
|
| 201 |
+
{sortedModels.map((model) => {
|
| 202 |
+
const hasStats = model.likes > 0 || model.downloads > 0
|
| 203 |
+
|
| 204 |
+
return (
|
| 205 |
+
<ListboxOption
|
| 206 |
+
key={model.id}
|
| 207 |
+
value={model}
|
| 208 |
+
className={({ active, selected }) =>
|
| 209 |
+
`px-3 py-2 cursor-pointer border-b border-gray-100 last:border-b-0 ${
|
| 210 |
+
active ? 'bg-gray-50' : ''
|
| 211 |
+
} ${selected ? 'bg-blue-50' : ''}`
|
| 212 |
+
}
|
| 213 |
+
>
|
| 214 |
+
{({ selected }) => (
|
| 215 |
+
<div className="flex items-center justify-between">
|
| 216 |
+
<div className="flex items-center flex-1 mr-2">
|
| 217 |
+
<span className="text-sm font-medium truncate">
|
| 218 |
+
{model.id}
|
| 219 |
+
</span>
|
| 220 |
+
{selected && (
|
| 221 |
+
<Check className="w-4 h-4 text-blue-600 ml-2 flex-shrink-0" />
|
| 222 |
+
)}
|
| 223 |
+
</div>
|
| 224 |
+
|
| 225 |
+
{/* Stats Display */}
|
| 226 |
+
{hasStats && (
|
| 227 |
+
<div className="flex items-center space-x-3 text-xs text-gray-500 flex-shrink-0">
|
| 228 |
+
{model.likes > 0 && (
|
| 229 |
+
<div className="flex items-center space-x-1">
|
| 230 |
+
<Heart className="w-3 h-3 text-red-500" />
|
| 231 |
+
<span>{formatNumber(model.likes)}</span>
|
| 232 |
+
</div>
|
| 233 |
+
)}
|
| 234 |
+
|
| 235 |
+
{model.downloads > 0 && (
|
| 236 |
+
<div className="flex items-center space-x-1">
|
| 237 |
+
<Download className="w-3 h-3 text-green-500" />
|
| 238 |
+
<span>{formatNumber(model.downloads)}</span>
|
| 239 |
+
</div>
|
| 240 |
+
)}
|
| 241 |
+
|
| 242 |
+
{model.createdAt && (
|
| 243 |
+
<span className="text-xs text-gray-400">
|
| 244 |
+
{model.createdAt.split('T')[0]}
|
| 245 |
+
</span>
|
| 246 |
+
)}
|
| 247 |
+
</div>
|
| 248 |
+
)}
|
| 249 |
</div>
|
| 250 |
)}
|
| 251 |
+
</ListboxOption>
|
| 252 |
+
)
|
| 253 |
+
})}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
</div>
|
| 255 |
+
</ListboxOptions>
|
| 256 |
+
</Transition>
|
| 257 |
</div>
|
| 258 |
+
</Listbox>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
</div>
|
| 260 |
)
|
| 261 |
}
|
src/components/PipelineSelector.tsx
CHANGED
|
@@ -1,4 +1,12 @@
|
|
| 1 |
import React from 'react';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
const pipelines = [
|
| 4 |
'zero-shot-classification',
|
|
@@ -21,14 +29,73 @@ const PipelineSelector: React.FC<PipelineSelectorProps> = ({
|
|
| 21 |
pipeline,
|
| 22 |
setPipeline
|
| 23 |
}) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
return (
|
| 25 |
-
<
|
| 26 |
-
{
|
| 27 |
-
<
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
);
|
| 33 |
};
|
| 34 |
|
|
|
|
| 1 |
import React from 'react';
|
| 2 |
+
import {
|
| 3 |
+
Listbox,
|
| 4 |
+
ListboxOption,
|
| 5 |
+
ListboxButton,
|
| 6 |
+
ListboxOptions,
|
| 7 |
+
Transition
|
| 8 |
+
} from '@headlessui/react'
|
| 9 |
+
import { ChevronDown, Check } from 'lucide-react';
|
| 10 |
|
| 11 |
const pipelines = [
|
| 12 |
'zero-shot-classification',
|
|
|
|
| 29 |
pipeline,
|
| 30 |
setPipeline
|
| 31 |
}) => {
|
| 32 |
+
const selectedPipeline = pipeline;
|
| 33 |
+
|
| 34 |
+
const formatPipelineName = (pipelineId: string) => {
|
| 35 |
+
return pipelineId
|
| 36 |
+
.split('-')
|
| 37 |
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
| 38 |
+
.join(' ');
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
return (
|
| 42 |
+
<div className="relative">
|
| 43 |
+
<Listbox value={selectedPipeline} onChange={setPipeline}>
|
| 44 |
+
<div className="relative">
|
| 45 |
+
<ListboxButton className="relative w-full cursor-default rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm border border-gray-300">
|
| 46 |
+
<span className="block truncate font-medium">
|
| 47 |
+
{formatPipelineName(selectedPipeline)}
|
| 48 |
+
</span>
|
| 49 |
+
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
| 50 |
+
<ChevronDown
|
| 51 |
+
className="h-5 w-5 text-gray-400 ui-open:rotate-180 transition-transform"
|
| 52 |
+
aria-hidden="true"
|
| 53 |
+
/>
|
| 54 |
+
</span>
|
| 55 |
+
</ListboxButton>
|
| 56 |
+
|
| 57 |
+
<Transition
|
| 58 |
+
enter="transition duration-100 ease-out"
|
| 59 |
+
enterFrom="transform scale-95 opacity-0"
|
| 60 |
+
enterTo="transform scale-100 opacity-100"
|
| 61 |
+
leave="transition duration-75 ease-out"
|
| 62 |
+
leaveFrom="transform scale-100 opacity-100"
|
| 63 |
+
leaveTo="transform scale-95 opacity-0"
|
| 64 |
+
>
|
| 65 |
+
<ListboxOptions className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
| 66 |
+
{pipelines.map((p) => (
|
| 67 |
+
<ListboxOption
|
| 68 |
+
key={p}
|
| 69 |
+
className={({ active }) =>
|
| 70 |
+
`relative cursor-default select-none py-2 px-4 ${
|
| 71 |
+
active ? 'bg-amber-100 text-amber-900' : 'text-gray-900'
|
| 72 |
+
}`
|
| 73 |
+
}
|
| 74 |
+
value={p}
|
| 75 |
+
>
|
| 76 |
+
{({ selected }) => (
|
| 77 |
+
<div className="flex items-center justify-between">
|
| 78 |
+
<span
|
| 79 |
+
className={`block truncate ${
|
| 80 |
+
selected ? 'font-medium' : 'font-normal'
|
| 81 |
+
}`}
|
| 82 |
+
>
|
| 83 |
+
{formatPipelineName(p)}
|
| 84 |
+
</span>
|
| 85 |
+
{selected && (
|
| 86 |
+
<span className="flex items-center text-amber-600">
|
| 87 |
+
<Check className="h-5 w-5" aria-hidden="true" />
|
| 88 |
+
</span>
|
| 89 |
+
)}
|
| 90 |
+
</div>
|
| 91 |
+
)}
|
| 92 |
+
</ListboxOption>
|
| 93 |
+
))}
|
| 94 |
+
</ListboxOptions>
|
| 95 |
+
</Transition>
|
| 96 |
+
</div>
|
| 97 |
+
</Listbox>
|
| 98 |
+
</div>
|
| 99 |
);
|
| 100 |
};
|
| 101 |
|
src/components/ZeroShotClassification.tsx
CHANGED
|
@@ -52,37 +52,6 @@ function ZeroShotClassification() {
|
|
| 52 |
)
|
| 53 |
|
| 54 |
const { setProgress, status, setStatus, modelInfo, setModelInfo } = useModel()
|
| 55 |
-
useEffect(() => {
|
| 56 |
-
const modelName = 'lxyuan/distilbert-base-multilingual-cased-sentiments-student'
|
| 57 |
-
const fetchModelInfo = async () => {
|
| 58 |
-
try {
|
| 59 |
-
const modelInfoResponse = await getModelInfo(modelName)
|
| 60 |
-
console.log(modelInfoResponse)
|
| 61 |
-
let parameters = 0
|
| 62 |
-
if (modelInfoResponse.safetensors) {
|
| 63 |
-
const safetensors = modelInfoResponse.safetensors
|
| 64 |
-
parameters =
|
| 65 |
-
safetensors.parameters.F16 ||
|
| 66 |
-
safetensors.parameters.F32 ||
|
| 67 |
-
safetensors.parameters.total ||
|
| 68 |
-
0
|
| 69 |
-
}
|
| 70 |
-
setModelInfo({
|
| 71 |
-
id: modelInfoResponse.id,
|
| 72 |
-
name: modelName,
|
| 73 |
-
architecture: modelInfoResponse.config?.architectures[0] ?? '',
|
| 74 |
-
parameters,
|
| 75 |
-
likes: modelInfoResponse.likes,
|
| 76 |
-
downloads: modelInfoResponse.downloads,
|
| 77 |
-
createdAt: modelInfoResponse.createdAt,
|
| 78 |
-
})
|
| 79 |
-
} catch (error) {
|
| 80 |
-
console.error('Error fetching model info:', error)
|
| 81 |
-
}
|
| 82 |
-
}
|
| 83 |
-
|
| 84 |
-
fetchModelInfo()
|
| 85 |
-
}, [setModelInfo])
|
| 86 |
|
| 87 |
// Create a reference to the worker object.
|
| 88 |
const worker = useRef<Worker | null>(null)
|
|
|
|
| 52 |
)
|
| 53 |
|
| 54 |
const { setProgress, status, setStatus, modelInfo, setModelInfo } = useModel()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
// Create a reference to the worker object.
|
| 57 |
const worker = useRef<Worker | null>(null)
|
src/contexts/ModelContext.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
import React, { createContext, useContext, useEffect, useState } from 'react'
|
| 2 |
-
import { ModelInfo, ModelInfoResponse } from '../types'
|
| 3 |
|
| 4 |
interface ModelContextType {
|
| 5 |
progress: number
|
|
@@ -12,6 +12,8 @@ interface ModelContextType {
|
|
| 12 |
setPipeline: (pipeline: string) => void
|
| 13 |
models: ModelInfoResponse[]
|
| 14 |
setModels: (models: ModelInfoResponse[]) => void
|
|
|
|
|
|
|
| 15 |
}
|
| 16 |
|
| 17 |
const ModelContext = createContext<ModelContextType | undefined>(undefined)
|
|
@@ -22,6 +24,7 @@ export function ModelProvider({ children }: { children: React.ReactNode }) {
|
|
| 22 |
const [modelInfo, setModelInfo] = useState<ModelInfo>({} as ModelInfo)
|
| 23 |
const [models, setModels] = useState<ModelInfoResponse[]>([] as ModelInfoResponse[])
|
| 24 |
const [pipeline, setPipeline] = useState<string>('zero-shot-classification')
|
|
|
|
| 25 |
|
| 26 |
// set progress to 0 when model is changed
|
| 27 |
useEffect(() => {
|
|
@@ -41,6 +44,8 @@ export function ModelProvider({ children }: { children: React.ReactNode }) {
|
|
| 41 |
setModels,
|
| 42 |
pipeline,
|
| 43 |
setPipeline,
|
|
|
|
|
|
|
| 44 |
}}
|
| 45 |
>
|
| 46 |
{children}
|
|
|
|
| 1 |
import React, { createContext, useContext, useEffect, useState } from 'react'
|
| 2 |
+
import { ModelInfo, ModelInfoResponse, QuantizationType } from '../types'
|
| 3 |
|
| 4 |
interface ModelContextType {
|
| 5 |
progress: number
|
|
|
|
| 12 |
setPipeline: (pipeline: string) => void
|
| 13 |
models: ModelInfoResponse[]
|
| 14 |
setModels: (models: ModelInfoResponse[]) => void
|
| 15 |
+
selectedQuantization: QuantizationType
|
| 16 |
+
setSelectedQuantization: (quantization: QuantizationType) => void
|
| 17 |
}
|
| 18 |
|
| 19 |
const ModelContext = createContext<ModelContextType | undefined>(undefined)
|
|
|
|
| 24 |
const [modelInfo, setModelInfo] = useState<ModelInfo>({} as ModelInfo)
|
| 25 |
const [models, setModels] = useState<ModelInfoResponse[]>([] as ModelInfoResponse[])
|
| 26 |
const [pipeline, setPipeline] = useState<string>('zero-shot-classification')
|
| 27 |
+
const [selectedQuantization, setSelectedQuantization] = useState<QuantizationType>('int8')
|
| 28 |
|
| 29 |
// set progress to 0 when model is changed
|
| 30 |
useEffect(() => {
|
|
|
|
| 44 |
setModels,
|
| 45 |
pipeline,
|
| 46 |
setPipeline,
|
| 47 |
+
selectedQuantization,
|
| 48 |
+
setSelectedQuantization,
|
| 49 |
}}
|
| 50 |
>
|
| 51 |
{children}
|
src/lib/huggingface.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
-
import {
|
| 2 |
-
import { ModelInfoResponse } from "../types"
|
| 3 |
|
| 4 |
const getModelInfo = async (modelName: string): Promise<ModelInfoResponse> => {
|
| 5 |
const token = process.env.REACT_APP_HUGGINGFACE_TOKEN
|
|
@@ -23,7 +22,70 @@ const getModelInfo = async (modelName: string): Promise<ModelInfoResponse> => {
|
|
| 23 |
if (!response.ok) {
|
| 24 |
throw new Error(`Failed to fetch model info: ${response.statusText}`)
|
| 25 |
}
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
}
|
| 28 |
|
| 29 |
const getModelsByPipeline = async (
|
|
@@ -57,8 +119,6 @@ const getModelsByPipeline = async (
|
|
| 57 |
return models.slice(0, 10)
|
| 58 |
}
|
| 59 |
|
| 60 |
-
// Define the possible quantization types for clarity and type safety
|
| 61 |
-
type QuantizationType = 'FP32' | 'FP16' | 'INT8' | 'Q4'
|
| 62 |
function getModelSize(
|
| 63 |
parameters: number,
|
| 64 |
quantization: QuantizationType
|
|
@@ -66,20 +126,23 @@ function getModelSize(
|
|
| 66 |
let bytesPerParameter: number
|
| 67 |
|
| 68 |
switch (quantization) {
|
| 69 |
-
case '
|
| 70 |
// 32-bit floating point uses 4 bytes
|
| 71 |
bytesPerParameter = 4
|
| 72 |
break
|
| 73 |
-
case '
|
| 74 |
bytesPerParameter = 2
|
| 75 |
break
|
| 76 |
-
case '
|
|
|
|
|
|
|
|
|
|
| 77 |
bytesPerParameter = 1
|
| 78 |
break
|
| 79 |
-
case '
|
|
|
|
| 80 |
bytesPerParameter = 0.5
|
| 81 |
-
|
| 82 |
-
return theoreticalSize
|
| 83 |
}
|
| 84 |
|
| 85 |
// There are 1,024 * 1,024 bytes in a megabyte
|
|
@@ -91,4 +154,3 @@ function getModelSize(
|
|
| 91 |
|
| 92 |
|
| 93 |
export { getModelInfo, getModelSize, getModelsByPipeline }
|
| 94 |
-
|
|
|
|
| 1 |
+
import { ModelInfoResponse, QuantizationType } from "../types"
|
|
|
|
| 2 |
|
| 3 |
const getModelInfo = async (modelName: string): Promise<ModelInfoResponse> => {
|
| 4 |
const token = process.env.REACT_APP_HUGGINGFACE_TOKEN
|
|
|
|
| 22 |
if (!response.ok) {
|
| 23 |
throw new Error(`Failed to fetch model info: ${response.statusText}`)
|
| 24 |
}
|
| 25 |
+
|
| 26 |
+
const modelData: ModelInfoResponse = await response.json()
|
| 27 |
+
|
| 28 |
+
const requiredFiles = [
|
| 29 |
+
'config.json',
|
| 30 |
+
'tokenizer.json',
|
| 31 |
+
'tokenizer_config.json',
|
| 32 |
+
]
|
| 33 |
+
|
| 34 |
+
const siblingFiles = modelData.siblings?.map(s => s.rfilename) || []
|
| 35 |
+
const isCompatible =
|
| 36 |
+
requiredFiles.every((file) => siblingFiles.includes(file)) &&
|
| 37 |
+
siblingFiles.some((file) => file.endsWith('.onnx') && file.startsWith('onnx/'))
|
| 38 |
+
const incompatibilityReason = isCompatible
|
| 39 |
+
? ''
|
| 40 |
+
: `Missing required files: ${requiredFiles
|
| 41 |
+
.filter(file => !siblingFiles.includes(file))
|
| 42 |
+
.join(', ')}`
|
| 43 |
+
const supportedQuantizations = siblingFiles
|
| 44 |
+
.filter((file) => file.endsWith('.onnx') && file.includes('_'))
|
| 45 |
+
.map((file) => file.split('/')[1].split('_')[1].split('.')[0])
|
| 46 |
+
.filter((q) => q !== 'quantized')
|
| 47 |
+
const uniqueSupportedQuantizations = Array.from(new Set(supportedQuantizations))
|
| 48 |
+
uniqueSupportedQuantizations.sort((a, b) => {
|
| 49 |
+
const getNumericValue = (str: string) => {
|
| 50 |
+
const match = str.match(/(\d+)/)
|
| 51 |
+
return match ? parseInt(match[1]) : Infinity
|
| 52 |
+
}
|
| 53 |
+
return getNumericValue(a) - getNumericValue(b)
|
| 54 |
+
})
|
| 55 |
+
|
| 56 |
+
// If there's a base model, fetch its info and merge with compatibility data
|
| 57 |
+
const baseModel = modelData.cardData?.base_model ?? modelData.modelId
|
| 58 |
+
if (baseModel && !modelData.safetensors) {
|
| 59 |
+
const baseModelResponse = await fetch(
|
| 60 |
+
`https://huggingface.co/api/models/${baseModel}`,
|
| 61 |
+
{
|
| 62 |
+
method: 'GET',
|
| 63 |
+
headers: {
|
| 64 |
+
Authorization: `Bearer ${token}`
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
if (baseModelResponse.ok) {
|
| 70 |
+
const baseModelData: ModelInfoResponse = await baseModelResponse.json()
|
| 71 |
+
|
| 72 |
+
return {
|
| 73 |
+
...baseModelData,
|
| 74 |
+
id: modelData.id,
|
| 75 |
+
baseId: baseModel,
|
| 76 |
+
isCompatible,
|
| 77 |
+
incompatibilityReason,
|
| 78 |
+
supportedQuantizations: uniqueSupportedQuantizations as QuantizationType[]
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
return {
|
| 84 |
+
...modelData,
|
| 85 |
+
isCompatible,
|
| 86 |
+
incompatibilityReason,
|
| 87 |
+
supportedQuantizations: uniqueSupportedQuantizations as QuantizationType[]
|
| 88 |
+
}
|
| 89 |
}
|
| 90 |
|
| 91 |
const getModelsByPipeline = async (
|
|
|
|
| 119 |
return models.slice(0, 10)
|
| 120 |
}
|
| 121 |
|
|
|
|
|
|
|
| 122 |
function getModelSize(
|
| 123 |
parameters: number,
|
| 124 |
quantization: QuantizationType
|
|
|
|
| 126 |
let bytesPerParameter: number
|
| 127 |
|
| 128 |
switch (quantization) {
|
| 129 |
+
case 'fp32':
|
| 130 |
// 32-bit floating point uses 4 bytes
|
| 131 |
bytesPerParameter = 4
|
| 132 |
break
|
| 133 |
+
case 'fp16':
|
| 134 |
bytesPerParameter = 2
|
| 135 |
break
|
| 136 |
+
case 'int8':
|
| 137 |
+
case 'bnb8':
|
| 138 |
+
case 'uint8':
|
| 139 |
+
case 'q8':
|
| 140 |
bytesPerParameter = 1
|
| 141 |
break
|
| 142 |
+
case 'bnb4':
|
| 143 |
+
case 'q4':
|
| 144 |
bytesPerParameter = 0.5
|
| 145 |
+
break
|
|
|
|
| 146 |
}
|
| 147 |
|
| 148 |
// There are 1,024 * 1,024 bytes in a megabyte
|
|
|
|
| 154 |
|
| 155 |
|
| 156 |
export { getModelInfo, getModelSize, getModelsByPipeline }
|
|
|
src/types.ts
CHANGED
|
@@ -27,6 +27,14 @@ export interface TextClassificationWorkerInput {
|
|
| 27 |
|
| 28 |
export type AppStatus = 'idle' | 'loading' | 'processing'
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
export interface ModelInfo {
|
| 31 |
id: string
|
| 32 |
name: string
|
|
@@ -35,6 +43,10 @@ export interface ModelInfo {
|
|
| 35 |
likes: number
|
| 36 |
downloads: number
|
| 37 |
createdAt: string
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
}
|
| 39 |
|
| 40 |
|
|
@@ -48,6 +60,10 @@ export interface ModelInfoResponse {
|
|
| 48 |
lastModified: string
|
| 49 |
pipeline_tag: string
|
| 50 |
tags: string[]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
transformersInfo: {
|
| 52 |
pipeline_tag: string
|
| 53 |
auto_model: string
|
|
@@ -55,11 +71,19 @@ export interface ModelInfoResponse {
|
|
| 55 |
}
|
| 56 |
safetensors?: {
|
| 57 |
parameters: {
|
|
|
|
| 58 |
F16?: number
|
| 59 |
F32?: number
|
| 60 |
total?: number
|
| 61 |
}
|
| 62 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
likes: number
|
| 64 |
downloads: number
|
| 65 |
}
|
|
|
|
| 27 |
|
| 28 |
export type AppStatus = 'idle' | 'loading' | 'processing'
|
| 29 |
|
| 30 |
+
type q8 = 'q8' | 'int8' | 'bnb8' | 'uint8'
|
| 31 |
+
type q4 = 'q4' | 'bnb4'
|
| 32 |
+
type fp16 = 'fp16'
|
| 33 |
+
type fp32 = 'fp32'
|
| 34 |
+
|
| 35 |
+
export type QuantizationType = q8 | q4 | fp16 | fp32
|
| 36 |
+
|
| 37 |
+
|
| 38 |
export interface ModelInfo {
|
| 39 |
id: string
|
| 40 |
name: string
|
|
|
|
| 43 |
likes: number
|
| 44 |
downloads: number
|
| 45 |
createdAt: string
|
| 46 |
+
isCompatible?: boolean
|
| 47 |
+
incompatibilityReason?: string
|
| 48 |
+
supportedQuantizations: QuantizationType[]
|
| 49 |
+
baseId?: string
|
| 50 |
}
|
| 51 |
|
| 52 |
|
|
|
|
| 60 |
lastModified: string
|
| 61 |
pipeline_tag: string
|
| 62 |
tags: string[]
|
| 63 |
+
cardData?: {
|
| 64 |
+
base_model: string
|
| 65 |
+
}
|
| 66 |
+
baseId?: string
|
| 67 |
transformersInfo: {
|
| 68 |
pipeline_tag: string
|
| 69 |
auto_model: string
|
|
|
|
| 71 |
}
|
| 72 |
safetensors?: {
|
| 73 |
parameters: {
|
| 74 |
+
BF16?: number
|
| 75 |
F16?: number
|
| 76 |
F32?: number
|
| 77 |
total?: number
|
| 78 |
}
|
| 79 |
}
|
| 80 |
+
siblings?: {
|
| 81 |
+
rfilename: string
|
| 82 |
+
}[]
|
| 83 |
+
modelId?: string
|
| 84 |
+
isCompatible: boolean
|
| 85 |
+
incompatibilityReason?: string
|
| 86 |
+
supportedQuantizations: QuantizationType[]
|
| 87 |
likes: number
|
| 88 |
downloads: number
|
| 89 |
}
|