Mishig commited on
Commit
3382639
·
unverified ·
1 Parent(s): 46dd845

Revert "Generic Multimodal Support" (#1146)

Browse files

Revert "Generic Multimodal Support (#1021)"

This reverts commit 57f89346cc6d953246d0f1705e342e2e66fce386.

README.md CHANGED
@@ -230,7 +230,7 @@ The following is the default `chatPromptTemplate`, although newlines and indenti
230
 
231
  #### Multi modal model
232
 
233
- We currently support [IDEFICS](https://huggingface.co/blog/idefics) (hosted on TGI), OpenAI and Claude 3 as multimodal models. You can enable it by setting `multimodal: true` in your `MODELS` configuration. For IDEFICS, you must have a [PRO HF Api token](https://huggingface.co/settings/tokens). For OpenAI, see the [OpenAI section](#OpenAI). For Anthropic, see the [Anthropic section](#Anthropic).
234
 
235
  ```env
236
  {
@@ -465,34 +465,14 @@ MODELS=`[
465
 
466
  #### Anthropic
467
 
468
- We also support Anthropic models (including multimodal ones via `multmodal: true`) through the official SDK. You may provide your API key via the `ANTHROPIC_API_KEY` env variable, or alternatively, through the `endpoints.apiKey` as per the following example.
469
 
470
  ```
471
  MODELS=`[
472
- {
473
- "name": "claude-3-haiku-20240307",
474
- "displayName": "Claude 3 Haiku",
475
- "description": "Fastest and most compact model for near-instant responsiveness",
476
- "multimodal": true,
477
- "parameters": {
478
- "max_new_tokens": 4096,
479
- },
480
- "endpoints": [
481
- {
482
- "type": "anthropic",
483
- // optionals
484
- "apiKey": "sk-ant-...",
485
- "baseURL": "https://api.anthropic.com",
486
- "defaultHeaders": {},
487
- "defaultQuery": {}
488
- }
489
- ]
490
- },
491
  {
492
  "name": "claude-3-sonnet-20240229",
493
  "displayName": "Claude 3 Sonnet",
494
  "description": "Ideal balance of intelligence and speed",
495
- "multimodal": true,
496
  "parameters": {
497
  "max_new_tokens": 4096,
498
  },
@@ -511,7 +491,6 @@ MODELS=`[
511
  "name": "claude-3-opus-20240229",
512
  "displayName": "Claude 3 Opus",
513
  "description": "Most powerful model for highly complex tasks",
514
- "multimodal": true,
515
  "parameters": {
516
  "max_new_tokens": 4096
517
  },
@@ -537,7 +516,6 @@ MODELS=`[
537
  "name": "claude-3-sonnet@20240229",
538
  "displayName": "Claude 3 Sonnet",
539
  "description": "Ideal balance of intelligence and speed",
540
- "multimodal": true,
541
  "parameters": {
542
  "max_new_tokens": 4096,
543
  },
@@ -556,7 +534,6 @@ MODELS=`[
556
  "name": "claude-3-haiku@20240307",
557
  "displayName": "Claude 3 Haiku",
558
  "description": "Fastest, most compact model for near-instant responsiveness",
559
- "multimodal": true,
560
  "parameters": {
561
  "max_new_tokens": 4096
562
  },
 
230
 
231
  #### Multi modal model
232
 
233
+ We currently only support IDEFICS as a multimodal model, hosted on TGI. You can enable it by using the following config (if you have a PRO HF Api token):
234
 
235
  ```env
236
  {
 
465
 
466
  #### Anthropic
467
 
468
+ We also support Anthropic models through the official SDK. You may provide your API key via the `ANTHROPIC_API_KEY` env variable, or alternatively, through the `endpoints.apiKey` as per the following example.
469
 
470
  ```
471
  MODELS=`[
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  {
473
  "name": "claude-3-sonnet-20240229",
474
  "displayName": "Claude 3 Sonnet",
475
  "description": "Ideal balance of intelligence and speed",
 
476
  "parameters": {
477
  "max_new_tokens": 4096,
478
  },
 
491
  "name": "claude-3-opus-20240229",
492
  "displayName": "Claude 3 Opus",
493
  "description": "Most powerful model for highly complex tasks",
 
494
  "parameters": {
495
  "max_new_tokens": 4096
496
  },
 
516
  "name": "claude-3-sonnet@20240229",
517
  "displayName": "Claude 3 Sonnet",
518
  "description": "Ideal balance of intelligence and speed",
 
519
  "parameters": {
520
  "max_new_tokens": 4096,
521
  },
 
534
  "name": "claude-3-haiku@20240307",
535
  "displayName": "Claude 3 Haiku",
536
  "description": "Fastest, most compact model for near-instant responsiveness",
 
537
  "parameters": {
538
  "max_new_tokens": 4096
539
  },
package-lock.json CHANGED
@@ -13,14 +13,13 @@
13
  "@huggingface/inference": "^2.6.3",
14
  "@iconify-json/bi": "^1.1.21",
15
  "@playwright/browser-chromium": "^1.43.1",
16
- "@resvg/resvg-js": "^2.6.2",
17
  "@xenova/transformers": "^2.16.1",
18
  "autoprefixer": "^10.4.14",
19
  "browser-image-resizer": "^2.4.1",
20
  "date-fns": "^2.29.3",
21
  "dotenv": "^16.0.3",
22
  "express": "^4.19.2",
23
- "file-type": "^19.0.0",
24
  "handlebars": "^4.7.8",
25
  "highlight.js": "^11.7.0",
26
  "image-size": "^1.0.2",
@@ -42,7 +41,7 @@
42
  "satori-html": "^0.3.2",
43
  "sbd": "^1.0.19",
44
  "serpapi": "^1.1.1",
45
- "sharp": "^0.33.3",
46
  "tailwind-scrollbar": "^3.0.0",
47
  "tailwindcss": "^3.4.0",
48
  "uuid": "^9.0.1",
@@ -89,7 +88,7 @@
89
  "@google-cloud/vertexai": "^1.1.0",
90
  "aws4fetch": "^1.0.17",
91
  "cohere-ai": "^7.9.0",
92
- "openai": "^4.44.0"
93
  }
94
  },
95
  "node_modules/@alloc/quick-lru": {
@@ -238,9 +237,9 @@
238
  }
239
  },
240
  "node_modules/@emnapi/runtime": {
241
- "version": "1.1.1",
242
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz",
243
- "integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==",
244
  "optional": true,
245
  "dependencies": {
246
  "tslib": "^2.4.0"
@@ -812,9 +811,9 @@
812
  }
813
  },
814
  "node_modules/@img/sharp-darwin-arm64": {
815
- "version": "0.33.3",
816
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.3.tgz",
817
- "integrity": "sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==",
818
  "cpu": [
819
  "arm64"
820
  ],
@@ -833,13 +832,13 @@
833
  "url": "https://opencollective.com/libvips"
834
  },
835
  "optionalDependencies": {
836
- "@img/sharp-libvips-darwin-arm64": "1.0.2"
837
  }
838
  },
839
  "node_modules/@img/sharp-darwin-x64": {
840
- "version": "0.33.3",
841
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.3.tgz",
842
- "integrity": "sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==",
843
  "cpu": [
844
  "x64"
845
  ],
@@ -858,13 +857,13 @@
858
  "url": "https://opencollective.com/libvips"
859
  },
860
  "optionalDependencies": {
861
- "@img/sharp-libvips-darwin-x64": "1.0.2"
862
  }
863
  },
864
  "node_modules/@img/sharp-libvips-darwin-arm64": {
865
- "version": "1.0.2",
866
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz",
867
- "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==",
868
  "cpu": [
869
  "arm64"
870
  ],
@@ -883,9 +882,9 @@
883
  }
884
  },
885
  "node_modules/@img/sharp-libvips-darwin-x64": {
886
- "version": "1.0.2",
887
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz",
888
- "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==",
889
  "cpu": [
890
  "x64"
891
  ],
@@ -904,9 +903,9 @@
904
  }
905
  },
906
  "node_modules/@img/sharp-libvips-linux-arm": {
907
- "version": "1.0.2",
908
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz",
909
- "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==",
910
  "cpu": [
911
  "arm"
912
  ],
@@ -925,9 +924,9 @@
925
  }
926
  },
927
  "node_modules/@img/sharp-libvips-linux-arm64": {
928
- "version": "1.0.2",
929
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz",
930
- "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==",
931
  "cpu": [
932
  "arm64"
933
  ],
@@ -946,9 +945,9 @@
946
  }
947
  },
948
  "node_modules/@img/sharp-libvips-linux-s390x": {
949
- "version": "1.0.2",
950
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz",
951
- "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==",
952
  "cpu": [
953
  "s390x"
954
  ],
@@ -967,9 +966,9 @@
967
  }
968
  },
969
  "node_modules/@img/sharp-libvips-linux-x64": {
970
- "version": "1.0.2",
971
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz",
972
- "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==",
973
  "cpu": [
974
  "x64"
975
  ],
@@ -988,9 +987,9 @@
988
  }
989
  },
990
  "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
991
- "version": "1.0.2",
992
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz",
993
- "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==",
994
  "cpu": [
995
  "arm64"
996
  ],
@@ -1009,9 +1008,9 @@
1009
  }
1010
  },
1011
  "node_modules/@img/sharp-libvips-linuxmusl-x64": {
1012
- "version": "1.0.2",
1013
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz",
1014
- "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==",
1015
  "cpu": [
1016
  "x64"
1017
  ],
@@ -1030,9 +1029,9 @@
1030
  }
1031
  },
1032
  "node_modules/@img/sharp-linux-arm": {
1033
- "version": "0.33.3",
1034
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.3.tgz",
1035
- "integrity": "sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==",
1036
  "cpu": [
1037
  "arm"
1038
  ],
@@ -1051,13 +1050,13 @@
1051
  "url": "https://opencollective.com/libvips"
1052
  },
1053
  "optionalDependencies": {
1054
- "@img/sharp-libvips-linux-arm": "1.0.2"
1055
  }
1056
  },
1057
  "node_modules/@img/sharp-linux-arm64": {
1058
- "version": "0.33.3",
1059
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.3.tgz",
1060
- "integrity": "sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==",
1061
  "cpu": [
1062
  "arm64"
1063
  ],
@@ -1076,13 +1075,13 @@
1076
  "url": "https://opencollective.com/libvips"
1077
  },
1078
  "optionalDependencies": {
1079
- "@img/sharp-libvips-linux-arm64": "1.0.2"
1080
  }
1081
  },
1082
  "node_modules/@img/sharp-linux-s390x": {
1083
- "version": "0.33.3",
1084
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.3.tgz",
1085
- "integrity": "sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==",
1086
  "cpu": [
1087
  "s390x"
1088
  ],
@@ -1101,13 +1100,13 @@
1101
  "url": "https://opencollective.com/libvips"
1102
  },
1103
  "optionalDependencies": {
1104
- "@img/sharp-libvips-linux-s390x": "1.0.2"
1105
  }
1106
  },
1107
  "node_modules/@img/sharp-linux-x64": {
1108
- "version": "0.33.3",
1109
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.3.tgz",
1110
- "integrity": "sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==",
1111
  "cpu": [
1112
  "x64"
1113
  ],
@@ -1126,13 +1125,13 @@
1126
  "url": "https://opencollective.com/libvips"
1127
  },
1128
  "optionalDependencies": {
1129
- "@img/sharp-libvips-linux-x64": "1.0.2"
1130
  }
1131
  },
1132
  "node_modules/@img/sharp-linuxmusl-arm64": {
1133
- "version": "0.33.3",
1134
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.3.tgz",
1135
- "integrity": "sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==",
1136
  "cpu": [
1137
  "arm64"
1138
  ],
@@ -1151,13 +1150,13 @@
1151
  "url": "https://opencollective.com/libvips"
1152
  },
1153
  "optionalDependencies": {
1154
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.2"
1155
  }
1156
  },
1157
  "node_modules/@img/sharp-linuxmusl-x64": {
1158
- "version": "0.33.3",
1159
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.3.tgz",
1160
- "integrity": "sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==",
1161
  "cpu": [
1162
  "x64"
1163
  ],
@@ -1176,19 +1175,19 @@
1176
  "url": "https://opencollective.com/libvips"
1177
  },
1178
  "optionalDependencies": {
1179
- "@img/sharp-libvips-linuxmusl-x64": "1.0.2"
1180
  }
1181
  },
1182
  "node_modules/@img/sharp-wasm32": {
1183
- "version": "0.33.3",
1184
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.3.tgz",
1185
- "integrity": "sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==",
1186
  "cpu": [
1187
  "wasm32"
1188
  ],
1189
  "optional": true,
1190
  "dependencies": {
1191
- "@emnapi/runtime": "^1.1.0"
1192
  },
1193
  "engines": {
1194
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
@@ -1201,9 +1200,9 @@
1201
  }
1202
  },
1203
  "node_modules/@img/sharp-win32-ia32": {
1204
- "version": "0.33.3",
1205
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.3.tgz",
1206
- "integrity": "sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==",
1207
  "cpu": [
1208
  "ia32"
1209
  ],
@@ -1222,9 +1221,9 @@
1222
  }
1223
  },
1224
  "node_modules/@img/sharp-win32-x64": {
1225
- "version": "0.33.3",
1226
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.3.tgz",
1227
- "integrity": "sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==",
1228
  "cpu": [
1229
  "x64"
1230
  ],
@@ -1445,31 +1444,31 @@
1445
  "integrity": "sha512-yvwa+aCyYI/UjeD39BnpMypG8N06l86wIDW1/PAc6ihBRnodIfZDwccxQN3n1t74wduzaz74m4ZMHZnB06567Q=="
1446
  },
1447
  "node_modules/@resvg/resvg-js": {
1448
- "version": "2.6.2",
1449
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz",
1450
- "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==",
1451
  "engines": {
1452
  "node": ">= 10"
1453
  },
1454
  "optionalDependencies": {
1455
- "@resvg/resvg-js-android-arm-eabi": "2.6.2",
1456
- "@resvg/resvg-js-android-arm64": "2.6.2",
1457
- "@resvg/resvg-js-darwin-arm64": "2.6.2",
1458
- "@resvg/resvg-js-darwin-x64": "2.6.2",
1459
- "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2",
1460
- "@resvg/resvg-js-linux-arm64-gnu": "2.6.2",
1461
- "@resvg/resvg-js-linux-arm64-musl": "2.6.2",
1462
- "@resvg/resvg-js-linux-x64-gnu": "2.6.2",
1463
- "@resvg/resvg-js-linux-x64-musl": "2.6.2",
1464
- "@resvg/resvg-js-win32-arm64-msvc": "2.6.2",
1465
- "@resvg/resvg-js-win32-ia32-msvc": "2.6.2",
1466
- "@resvg/resvg-js-win32-x64-msvc": "2.6.2"
1467
  }
1468
  },
1469
  "node_modules/@resvg/resvg-js-android-arm-eabi": {
1470
- "version": "2.6.2",
1471
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz",
1472
- "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==",
1473
  "cpu": [
1474
  "arm"
1475
  ],
@@ -1482,9 +1481,9 @@
1482
  }
1483
  },
1484
  "node_modules/@resvg/resvg-js-android-arm64": {
1485
- "version": "2.6.2",
1486
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz",
1487
- "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==",
1488
  "cpu": [
1489
  "arm64"
1490
  ],
@@ -1497,9 +1496,9 @@
1497
  }
1498
  },
1499
  "node_modules/@resvg/resvg-js-darwin-arm64": {
1500
- "version": "2.6.2",
1501
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.2.tgz",
1502
- "integrity": "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==",
1503
  "cpu": [
1504
  "arm64"
1505
  ],
@@ -1512,9 +1511,9 @@
1512
  }
1513
  },
1514
  "node_modules/@resvg/resvg-js-darwin-x64": {
1515
- "version": "2.6.2",
1516
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz",
1517
- "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==",
1518
  "cpu": [
1519
  "x64"
1520
  ],
@@ -1527,9 +1526,9 @@
1527
  }
1528
  },
1529
  "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": {
1530
- "version": "2.6.2",
1531
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz",
1532
- "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==",
1533
  "cpu": [
1534
  "arm"
1535
  ],
@@ -1542,9 +1541,9 @@
1542
  }
1543
  },
1544
  "node_modules/@resvg/resvg-js-linux-arm64-gnu": {
1545
- "version": "2.6.2",
1546
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz",
1547
- "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==",
1548
  "cpu": [
1549
  "arm64"
1550
  ],
@@ -1557,9 +1556,9 @@
1557
  }
1558
  },
1559
  "node_modules/@resvg/resvg-js-linux-arm64-musl": {
1560
- "version": "2.6.2",
1561
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz",
1562
- "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==",
1563
  "cpu": [
1564
  "arm64"
1565
  ],
@@ -1572,9 +1571,9 @@
1572
  }
1573
  },
1574
  "node_modules/@resvg/resvg-js-linux-x64-gnu": {
1575
- "version": "2.6.2",
1576
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz",
1577
- "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==",
1578
  "cpu": [
1579
  "x64"
1580
  ],
@@ -1587,9 +1586,9 @@
1587
  }
1588
  },
1589
  "node_modules/@resvg/resvg-js-linux-x64-musl": {
1590
- "version": "2.6.2",
1591
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz",
1592
- "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==",
1593
  "cpu": [
1594
  "x64"
1595
  ],
@@ -1602,9 +1601,9 @@
1602
  }
1603
  },
1604
  "node_modules/@resvg/resvg-js-win32-arm64-msvc": {
1605
- "version": "2.6.2",
1606
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz",
1607
- "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==",
1608
  "cpu": [
1609
  "arm64"
1610
  ],
@@ -1617,9 +1616,9 @@
1617
  }
1618
  },
1619
  "node_modules/@resvg/resvg-js-win32-ia32-msvc": {
1620
- "version": "2.6.2",
1621
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz",
1622
- "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==",
1623
  "cpu": [
1624
  "ia32"
1625
  ],
@@ -1632,9 +1631,9 @@
1632
  }
1633
  },
1634
  "node_modules/@resvg/resvg-js-win32-x64-msvc": {
1635
- "version": "2.6.2",
1636
- "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz",
1637
- "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==",
1638
  "cpu": [
1639
  "x64"
1640
  ],
@@ -2076,11 +2075,6 @@
2076
  "node": ">=4"
2077
  }
2078
  },
2079
- "node_modules/@tokenizer/token": {
2080
- "version": "0.3.0",
2081
- "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
2082
- "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
2083
- },
2084
  "node_modules/@tootallnate/once": {
2085
  "version": "2.0.0",
2086
  "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -3837,9 +3831,9 @@
3837
  }
3838
  },
3839
  "node_modules/detect-libc": {
3840
- "version": "2.0.3",
3841
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
3842
- "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
3843
  "engines": {
3844
  "node": ">=8"
3845
  }
@@ -4562,22 +4556,6 @@
4562
  "node": "^10.12.0 || >=12.0.0"
4563
  }
4564
  },
4565
- "node_modules/file-type": {
4566
- "version": "19.0.0",
4567
- "resolved": "https://registry.npmjs.org/file-type/-/file-type-19.0.0.tgz",
4568
- "integrity": "sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==",
4569
- "dependencies": {
4570
- "readable-web-to-node-stream": "^3.0.2",
4571
- "strtok3": "^7.0.0",
4572
- "token-types": "^5.0.1"
4573
- },
4574
- "engines": {
4575
- "node": ">=18"
4576
- },
4577
- "funding": {
4578
- "url": "https://github.com/sindresorhus/file-type?sponsor=1"
4579
- }
4580
- },
4581
  "node_modules/fill-range": {
4582
  "version": "7.0.1",
4583
  "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -6407,15 +6385,16 @@
6407
  }
6408
  },
6409
  "node_modules/openai": {
6410
- "version": "4.47.1",
6411
- "resolved": "https://registry.npmjs.org/openai/-/openai-4.47.1.tgz",
6412
- "integrity": "sha512-WWSxhC/69ZhYWxH/OBsLEirIjUcfpQ5+ihkXKp06hmeYXgBBIUCa9IptMzYx6NdkiOCsSGYCnTIsxaic3AjRCQ==",
6413
  "optional": true,
6414
  "dependencies": {
6415
  "@types/node": "^18.11.18",
6416
  "@types/node-fetch": "^2.6.4",
6417
  "abort-controller": "^3.0.0",
6418
  "agentkeepalive": "^4.2.1",
 
6419
  "form-data-encoder": "1.7.2",
6420
  "formdata-node": "^4.3.2",
6421
  "node-fetch": "^2.6.7",
@@ -6641,18 +6620,6 @@
6641
  "node": "*"
6642
  }
6643
  },
6644
- "node_modules/peek-readable": {
6645
- "version": "5.0.0",
6646
- "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz",
6647
- "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==",
6648
- "engines": {
6649
- "node": ">=14.16"
6650
- },
6651
- "funding": {
6652
- "type": "github",
6653
- "url": "https://github.com/sponsors/Borewit"
6654
- }
6655
- },
6656
  "node_modules/periscopic": {
6657
  "version": "3.1.0",
6658
  "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
@@ -7508,21 +7475,6 @@
7508
  "node": ">= 6"
7509
  }
7510
  },
7511
- "node_modules/readable-web-to-node-stream": {
7512
- "version": "3.0.2",
7513
- "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
7514
- "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
7515
- "dependencies": {
7516
- "readable-stream": "^3.6.0"
7517
- },
7518
- "engines": {
7519
- "node": ">=8"
7520
- },
7521
- "funding": {
7522
- "type": "github",
7523
- "url": "https://github.com/sponsors/Borewit"
7524
- }
7525
- },
7526
  "node_modules/readdirp": {
7527
  "version": "3.6.0",
7528
  "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -7784,9 +7736,12 @@
7784
  "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="
7785
  },
7786
  "node_modules/semver": {
7787
- "version": "7.6.2",
7788
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
7789
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
 
 
 
7790
  "bin": {
7791
  "semver": "bin/semver.js"
7792
  },
@@ -7885,42 +7840,42 @@
7885
  "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
7886
  },
7887
  "node_modules/sharp": {
7888
- "version": "0.33.3",
7889
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.3.tgz",
7890
- "integrity": "sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A==",
7891
  "hasInstallScript": true,
7892
  "dependencies": {
7893
  "color": "^4.2.3",
7894
- "detect-libc": "^2.0.3",
7895
- "semver": "^7.6.0"
7896
  },
7897
  "engines": {
7898
- "libvips": ">=8.15.2",
7899
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
7900
  },
7901
  "funding": {
7902
  "url": "https://opencollective.com/libvips"
7903
  },
7904
  "optionalDependencies": {
7905
- "@img/sharp-darwin-arm64": "0.33.3",
7906
- "@img/sharp-darwin-x64": "0.33.3",
7907
- "@img/sharp-libvips-darwin-arm64": "1.0.2",
7908
- "@img/sharp-libvips-darwin-x64": "1.0.2",
7909
- "@img/sharp-libvips-linux-arm": "1.0.2",
7910
- "@img/sharp-libvips-linux-arm64": "1.0.2",
7911
- "@img/sharp-libvips-linux-s390x": "1.0.2",
7912
- "@img/sharp-libvips-linux-x64": "1.0.2",
7913
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.2",
7914
- "@img/sharp-libvips-linuxmusl-x64": "1.0.2",
7915
- "@img/sharp-linux-arm": "0.33.3",
7916
- "@img/sharp-linux-arm64": "0.33.3",
7917
- "@img/sharp-linux-s390x": "0.33.3",
7918
- "@img/sharp-linux-x64": "0.33.3",
7919
- "@img/sharp-linuxmusl-arm64": "0.33.3",
7920
- "@img/sharp-linuxmusl-x64": "0.33.3",
7921
- "@img/sharp-wasm32": "0.33.3",
7922
- "@img/sharp-win32-ia32": "0.33.3",
7923
- "@img/sharp-win32-x64": "0.33.3"
7924
  }
7925
  },
7926
  "node_modules/shebang-command": {
@@ -8232,22 +8187,6 @@
8232
  "url": "https://github.com/sponsors/antfu"
8233
  }
8234
  },
8235
- "node_modules/strtok3": {
8236
- "version": "7.0.0",
8237
- "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz",
8238
- "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==",
8239
- "dependencies": {
8240
- "@tokenizer/token": "^0.3.0",
8241
- "peek-readable": "^5.0.0"
8242
- },
8243
- "engines": {
8244
- "node": ">=14.16"
8245
- },
8246
- "funding": {
8247
- "type": "github",
8248
- "url": "https://github.com/sponsors/Borewit"
8249
- }
8250
- },
8251
  "node_modules/sucrase": {
8252
  "version": "3.32.0",
8253
  "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
@@ -8770,22 +8709,6 @@
8770
  "node": ">=0.6"
8771
  }
8772
  },
8773
- "node_modules/token-types": {
8774
- "version": "5.0.1",
8775
- "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz",
8776
- "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==",
8777
- "dependencies": {
8778
- "@tokenizer/token": "^0.3.0",
8779
- "ieee754": "^1.2.1"
8780
- },
8781
- "engines": {
8782
- "node": ">=14.16"
8783
- },
8784
- "funding": {
8785
- "type": "github",
8786
- "url": "https://github.com/sponsors/Borewit"
8787
- }
8788
- },
8789
  "node_modules/totalist": {
8790
  "version": "3.0.0",
8791
  "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
 
13
  "@huggingface/inference": "^2.6.3",
14
  "@iconify-json/bi": "^1.1.21",
15
  "@playwright/browser-chromium": "^1.43.1",
16
+ "@resvg/resvg-js": "^2.6.0",
17
  "@xenova/transformers": "^2.16.1",
18
  "autoprefixer": "^10.4.14",
19
  "browser-image-resizer": "^2.4.1",
20
  "date-fns": "^2.29.3",
21
  "dotenv": "^16.0.3",
22
  "express": "^4.19.2",
 
23
  "handlebars": "^4.7.8",
24
  "highlight.js": "^11.7.0",
25
  "image-size": "^1.0.2",
 
41
  "satori-html": "^0.3.2",
42
  "sbd": "^1.0.19",
43
  "serpapi": "^1.1.1",
44
+ "sharp": "^0.33.2",
45
  "tailwind-scrollbar": "^3.0.0",
46
  "tailwindcss": "^3.4.0",
47
  "uuid": "^9.0.1",
 
88
  "@google-cloud/vertexai": "^1.1.0",
89
  "aws4fetch": "^1.0.17",
90
  "cohere-ai": "^7.9.0",
91
+ "openai": "^4.14.2"
92
  }
93
  },
94
  "node_modules/@alloc/quick-lru": {
 
237
  }
238
  },
239
  "node_modules/@emnapi/runtime": {
240
+ "version": "0.45.0",
241
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz",
242
+ "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==",
243
  "optional": true,
244
  "dependencies": {
245
  "tslib": "^2.4.0"
 
811
  }
812
  },
813
  "node_modules/@img/sharp-darwin-arm64": {
814
+ "version": "0.33.2",
815
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz",
816
+ "integrity": "sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==",
817
  "cpu": [
818
  "arm64"
819
  ],
 
832
  "url": "https://opencollective.com/libvips"
833
  },
834
  "optionalDependencies": {
835
+ "@img/sharp-libvips-darwin-arm64": "1.0.1"
836
  }
837
  },
838
  "node_modules/@img/sharp-darwin-x64": {
839
+ "version": "0.33.2",
840
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz",
841
+ "integrity": "sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==",
842
  "cpu": [
843
  "x64"
844
  ],
 
857
  "url": "https://opencollective.com/libvips"
858
  },
859
  "optionalDependencies": {
860
+ "@img/sharp-libvips-darwin-x64": "1.0.1"
861
  }
862
  },
863
  "node_modules/@img/sharp-libvips-darwin-arm64": {
864
+ "version": "1.0.1",
865
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz",
866
+ "integrity": "sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==",
867
  "cpu": [
868
  "arm64"
869
  ],
 
882
  }
883
  },
884
  "node_modules/@img/sharp-libvips-darwin-x64": {
885
+ "version": "1.0.1",
886
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz",
887
+ "integrity": "sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==",
888
  "cpu": [
889
  "x64"
890
  ],
 
903
  }
904
  },
905
  "node_modules/@img/sharp-libvips-linux-arm": {
906
+ "version": "1.0.1",
907
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz",
908
+ "integrity": "sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==",
909
  "cpu": [
910
  "arm"
911
  ],
 
924
  }
925
  },
926
  "node_modules/@img/sharp-libvips-linux-arm64": {
927
+ "version": "1.0.1",
928
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz",
929
+ "integrity": "sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==",
930
  "cpu": [
931
  "arm64"
932
  ],
 
945
  }
946
  },
947
  "node_modules/@img/sharp-libvips-linux-s390x": {
948
+ "version": "1.0.1",
949
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz",
950
+ "integrity": "sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==",
951
  "cpu": [
952
  "s390x"
953
  ],
 
966
  }
967
  },
968
  "node_modules/@img/sharp-libvips-linux-x64": {
969
+ "version": "1.0.1",
970
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz",
971
+ "integrity": "sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==",
972
  "cpu": [
973
  "x64"
974
  ],
 
987
  }
988
  },
989
  "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
990
+ "version": "1.0.1",
991
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz",
992
+ "integrity": "sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==",
993
  "cpu": [
994
  "arm64"
995
  ],
 
1008
  }
1009
  },
1010
  "node_modules/@img/sharp-libvips-linuxmusl-x64": {
1011
+ "version": "1.0.1",
1012
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz",
1013
+ "integrity": "sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==",
1014
  "cpu": [
1015
  "x64"
1016
  ],
 
1029
  }
1030
  },
1031
  "node_modules/@img/sharp-linux-arm": {
1032
+ "version": "0.33.2",
1033
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz",
1034
+ "integrity": "sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==",
1035
  "cpu": [
1036
  "arm"
1037
  ],
 
1050
  "url": "https://opencollective.com/libvips"
1051
  },
1052
  "optionalDependencies": {
1053
+ "@img/sharp-libvips-linux-arm": "1.0.1"
1054
  }
1055
  },
1056
  "node_modules/@img/sharp-linux-arm64": {
1057
+ "version": "0.33.2",
1058
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz",
1059
+ "integrity": "sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==",
1060
  "cpu": [
1061
  "arm64"
1062
  ],
 
1075
  "url": "https://opencollective.com/libvips"
1076
  },
1077
  "optionalDependencies": {
1078
+ "@img/sharp-libvips-linux-arm64": "1.0.1"
1079
  }
1080
  },
1081
  "node_modules/@img/sharp-linux-s390x": {
1082
+ "version": "0.33.2",
1083
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz",
1084
+ "integrity": "sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==",
1085
  "cpu": [
1086
  "s390x"
1087
  ],
 
1100
  "url": "https://opencollective.com/libvips"
1101
  },
1102
  "optionalDependencies": {
1103
+ "@img/sharp-libvips-linux-s390x": "1.0.1"
1104
  }
1105
  },
1106
  "node_modules/@img/sharp-linux-x64": {
1107
+ "version": "0.33.2",
1108
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz",
1109
+ "integrity": "sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==",
1110
  "cpu": [
1111
  "x64"
1112
  ],
 
1125
  "url": "https://opencollective.com/libvips"
1126
  },
1127
  "optionalDependencies": {
1128
+ "@img/sharp-libvips-linux-x64": "1.0.1"
1129
  }
1130
  },
1131
  "node_modules/@img/sharp-linuxmusl-arm64": {
1132
+ "version": "0.33.2",
1133
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz",
1134
+ "integrity": "sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==",
1135
  "cpu": [
1136
  "arm64"
1137
  ],
 
1150
  "url": "https://opencollective.com/libvips"
1151
  },
1152
  "optionalDependencies": {
1153
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.1"
1154
  }
1155
  },
1156
  "node_modules/@img/sharp-linuxmusl-x64": {
1157
+ "version": "0.33.2",
1158
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz",
1159
+ "integrity": "sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==",
1160
  "cpu": [
1161
  "x64"
1162
  ],
 
1175
  "url": "https://opencollective.com/libvips"
1176
  },
1177
  "optionalDependencies": {
1178
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.1"
1179
  }
1180
  },
1181
  "node_modules/@img/sharp-wasm32": {
1182
+ "version": "0.33.2",
1183
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz",
1184
+ "integrity": "sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==",
1185
  "cpu": [
1186
  "wasm32"
1187
  ],
1188
  "optional": true,
1189
  "dependencies": {
1190
+ "@emnapi/runtime": "^0.45.0"
1191
  },
1192
  "engines": {
1193
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
 
1200
  }
1201
  },
1202
  "node_modules/@img/sharp-win32-ia32": {
1203
+ "version": "0.33.2",
1204
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz",
1205
+ "integrity": "sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==",
1206
  "cpu": [
1207
  "ia32"
1208
  ],
 
1221
  }
1222
  },
1223
  "node_modules/@img/sharp-win32-x64": {
1224
+ "version": "0.33.2",
1225
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz",
1226
+ "integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==",
1227
  "cpu": [
1228
  "x64"
1229
  ],
 
1444
  "integrity": "sha512-yvwa+aCyYI/UjeD39BnpMypG8N06l86wIDW1/PAc6ihBRnodIfZDwccxQN3n1t74wduzaz74m4ZMHZnB06567Q=="
1445
  },
1446
  "node_modules/@resvg/resvg-js": {
1447
+ "version": "2.6.0",
1448
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.0.tgz",
1449
+ "integrity": "sha512-Tf3YpbBKcQn991KKcw/vg7vZf98v01seSv6CVxZBbRkL/xyjnoYB6KgrFL6zskT1A4dWC/vg77KyNOW+ePaNlA==",
1450
  "engines": {
1451
  "node": ">= 10"
1452
  },
1453
  "optionalDependencies": {
1454
+ "@resvg/resvg-js-android-arm-eabi": "2.6.0",
1455
+ "@resvg/resvg-js-android-arm64": "2.6.0",
1456
+ "@resvg/resvg-js-darwin-arm64": "2.6.0",
1457
+ "@resvg/resvg-js-darwin-x64": "2.6.0",
1458
+ "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.0",
1459
+ "@resvg/resvg-js-linux-arm64-gnu": "2.6.0",
1460
+ "@resvg/resvg-js-linux-arm64-musl": "2.6.0",
1461
+ "@resvg/resvg-js-linux-x64-gnu": "2.6.0",
1462
+ "@resvg/resvg-js-linux-x64-musl": "2.6.0",
1463
+ "@resvg/resvg-js-win32-arm64-msvc": "2.6.0",
1464
+ "@resvg/resvg-js-win32-ia32-msvc": "2.6.0",
1465
+ "@resvg/resvg-js-win32-x64-msvc": "2.6.0"
1466
  }
1467
  },
1468
  "node_modules/@resvg/resvg-js-android-arm-eabi": {
1469
+ "version": "2.6.0",
1470
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.0.tgz",
1471
+ "integrity": "sha512-lJnZ/2P5aMocrFMW7HWhVne5gH82I8xH6zsfH75MYr4+/JOaVcGCTEQ06XFohGMdYRP3v05SSPLPvTM/RHjxfA==",
1472
  "cpu": [
1473
  "arm"
1474
  ],
 
1481
  }
1482
  },
1483
  "node_modules/@resvg/resvg-js-android-arm64": {
1484
+ "version": "2.6.0",
1485
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.0.tgz",
1486
+ "integrity": "sha512-N527f529bjMwYWShZYfBD60dXA4Fux+D695QsHQ93BDYZSHUoOh1CUGUyICevnTxs7VgEl98XpArmUWBZQVMfQ==",
1487
  "cpu": [
1488
  "arm64"
1489
  ],
 
1496
  }
1497
  },
1498
  "node_modules/@resvg/resvg-js-darwin-arm64": {
1499
+ "version": "2.6.0",
1500
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.0.tgz",
1501
+ "integrity": "sha512-MabUKLVayEwlPo0mIqAmMt+qESN8LltCvv5+GLgVga1avpUrkxj/fkU1TKm8kQegutUjbP/B0QuMuUr0uhF8ew==",
1502
  "cpu": [
1503
  "arm64"
1504
  ],
 
1511
  }
1512
  },
1513
  "node_modules/@resvg/resvg-js-darwin-x64": {
1514
+ "version": "2.6.0",
1515
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.0.tgz",
1516
+ "integrity": "sha512-zrFetdnSw/suXjmyxSjfDV7i61hahv6DDG6kM7BYN2yJ3Es5+BZtqYZTcIWogPJedYKmzN1YTMWGd/3f0ubFiA==",
1517
  "cpu": [
1518
  "x64"
1519
  ],
 
1526
  }
1527
  },
1528
  "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": {
1529
+ "version": "2.6.0",
1530
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.0.tgz",
1531
+ "integrity": "sha512-sH4gxXt7v7dGwjGyzLwn7SFGvwZG6DQqLaZ11MmzbCwd9Zosy1TnmrMJfn6TJ7RHezmQMgBPi18bl55FZ1AT4A==",
1532
  "cpu": [
1533
  "arm"
1534
  ],
 
1541
  }
1542
  },
1543
  "node_modules/@resvg/resvg-js-linux-arm64-gnu": {
1544
+ "version": "2.6.0",
1545
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.0.tgz",
1546
+ "integrity": "sha512-fCyMncqCJtrlANADIduYF4IfnWQ295UKib7DAxFXQhBsM9PLDTpizr0qemZcCNadcwSVHnAIzL4tliZhCM8P6A==",
1547
  "cpu": [
1548
  "arm64"
1549
  ],
 
1556
  }
1557
  },
1558
  "node_modules/@resvg/resvg-js-linux-arm64-musl": {
1559
+ "version": "2.6.0",
1560
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.0.tgz",
1561
+ "integrity": "sha512-ouLjTgBQHQyxLht4FdMPTvuY8xzJigM9EM2Tlu0llWkN1mKyTQrvYWi6TA6XnKdzDJHy7ZLpWpjZi7F5+Pg+Vg==",
1562
  "cpu": [
1563
  "arm64"
1564
  ],
 
1571
  }
1572
  },
1573
  "node_modules/@resvg/resvg-js-linux-x64-gnu": {
1574
+ "version": "2.6.0",
1575
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.0.tgz",
1576
+ "integrity": "sha512-n3zC8DWsvxC1AwxpKFclIPapDFibs5XdIRoV/mcIlxlh0vseW1F49b97F33BtJQRmlntsqqN6GMMqx8byB7B+Q==",
1577
  "cpu": [
1578
  "x64"
1579
  ],
 
1586
  }
1587
  },
1588
  "node_modules/@resvg/resvg-js-linux-x64-musl": {
1589
+ "version": "2.6.0",
1590
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.0.tgz",
1591
+ "integrity": "sha512-n4tasK1HOlAxdTEROgYA1aCfsEKk0UOFDNd/AQTTZlTmCbHKXPq+O8npaaKlwXquxlVK8vrkcWbksbiGqbCAcw==",
1592
  "cpu": [
1593
  "x64"
1594
  ],
 
1601
  }
1602
  },
1603
  "node_modules/@resvg/resvg-js-win32-arm64-msvc": {
1604
+ "version": "2.6.0",
1605
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.0.tgz",
1606
+ "integrity": "sha512-X2+EoBJFwDI5LDVb51Sk7ldnVLitMGr9WwU/i21i3fAeAXZb3hM16k67DeTy16OYkT2dk/RfU1tP1wG+rWbz2Q==",
1607
  "cpu": [
1608
  "arm64"
1609
  ],
 
1616
  }
1617
  },
1618
  "node_modules/@resvg/resvg-js-win32-ia32-msvc": {
1619
+ "version": "2.6.0",
1620
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.0.tgz",
1621
+ "integrity": "sha512-L7oevWjQoUgK5W1fCKn0euSVemhDXVhrjtwqpc7MwBKKimYeiOshO1Li1pa8bBt5PESahenhWgdB6lav9O0fEg==",
1622
  "cpu": [
1623
  "ia32"
1624
  ],
 
1631
  }
1632
  },
1633
  "node_modules/@resvg/resvg-js-win32-x64-msvc": {
1634
+ "version": "2.6.0",
1635
+ "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.0.tgz",
1636
+ "integrity": "sha512-8lJlghb+Unki5AyKgsnFbRJwkEj9r1NpwyuBG8yEJiG1W9eEGl03R3I7bsVa3haof/3J1NlWf0rzSa1G++A2iw==",
1637
  "cpu": [
1638
  "x64"
1639
  ],
 
2075
  "node": ">=4"
2076
  }
2077
  },
 
 
 
 
 
2078
  "node_modules/@tootallnate/once": {
2079
  "version": "2.0.0",
2080
  "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
 
3831
  }
3832
  },
3833
  "node_modules/detect-libc": {
3834
+ "version": "2.0.2",
3835
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
3836
+ "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
3837
  "engines": {
3838
  "node": ">=8"
3839
  }
 
4556
  "node": "^10.12.0 || >=12.0.0"
4557
  }
4558
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4559
  "node_modules/fill-range": {
4560
  "version": "7.0.1",
4561
  "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
 
6385
  }
6386
  },
6387
  "node_modules/openai": {
6388
+ "version": "4.14.2",
6389
+ "resolved": "https://registry.npmjs.org/openai/-/openai-4.14.2.tgz",
6390
+ "integrity": "sha512-JGlm7mMC7J+cyQZnQMOH7daD9cBqqWqLtlBsejElEkgoehPrYfdyxSxIGICz5xk4YimbwI5FlLATSVojLtCKXQ==",
6391
  "optional": true,
6392
  "dependencies": {
6393
  "@types/node": "^18.11.18",
6394
  "@types/node-fetch": "^2.6.4",
6395
  "abort-controller": "^3.0.0",
6396
  "agentkeepalive": "^4.2.1",
6397
+ "digest-fetch": "^1.3.0",
6398
  "form-data-encoder": "1.7.2",
6399
  "formdata-node": "^4.3.2",
6400
  "node-fetch": "^2.6.7",
 
6620
  "node": "*"
6621
  }
6622
  },
 
 
 
 
 
 
 
 
 
 
 
 
6623
  "node_modules/periscopic": {
6624
  "version": "3.1.0",
6625
  "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
 
7475
  "node": ">= 6"
7476
  }
7477
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7478
  "node_modules/readdirp": {
7479
  "version": "3.6.0",
7480
  "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
 
7736
  "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="
7737
  },
7738
  "node_modules/semver": {
7739
+ "version": "7.5.4",
7740
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
7741
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
7742
+ "dependencies": {
7743
+ "lru-cache": "^6.0.0"
7744
+ },
7745
  "bin": {
7746
  "semver": "bin/semver.js"
7747
  },
 
7840
  "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
7841
  },
7842
  "node_modules/sharp": {
7843
+ "version": "0.33.2",
7844
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.2.tgz",
7845
+ "integrity": "sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==",
7846
  "hasInstallScript": true,
7847
  "dependencies": {
7848
  "color": "^4.2.3",
7849
+ "detect-libc": "^2.0.2",
7850
+ "semver": "^7.5.4"
7851
  },
7852
  "engines": {
7853
+ "libvips": ">=8.15.1",
7854
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
7855
  },
7856
  "funding": {
7857
  "url": "https://opencollective.com/libvips"
7858
  },
7859
  "optionalDependencies": {
7860
+ "@img/sharp-darwin-arm64": "0.33.2",
7861
+ "@img/sharp-darwin-x64": "0.33.2",
7862
+ "@img/sharp-libvips-darwin-arm64": "1.0.1",
7863
+ "@img/sharp-libvips-darwin-x64": "1.0.1",
7864
+ "@img/sharp-libvips-linux-arm": "1.0.1",
7865
+ "@img/sharp-libvips-linux-arm64": "1.0.1",
7866
+ "@img/sharp-libvips-linux-s390x": "1.0.1",
7867
+ "@img/sharp-libvips-linux-x64": "1.0.1",
7868
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.1",
7869
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.1",
7870
+ "@img/sharp-linux-arm": "0.33.2",
7871
+ "@img/sharp-linux-arm64": "0.33.2",
7872
+ "@img/sharp-linux-s390x": "0.33.2",
7873
+ "@img/sharp-linux-x64": "0.33.2",
7874
+ "@img/sharp-linuxmusl-arm64": "0.33.2",
7875
+ "@img/sharp-linuxmusl-x64": "0.33.2",
7876
+ "@img/sharp-wasm32": "0.33.2",
7877
+ "@img/sharp-win32-ia32": "0.33.2",
7878
+ "@img/sharp-win32-x64": "0.33.2"
7879
  }
7880
  },
7881
  "node_modules/shebang-command": {
 
8187
  "url": "https://github.com/sponsors/antfu"
8188
  }
8189
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8190
  "node_modules/sucrase": {
8191
  "version": "3.32.0",
8192
  "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
 
8709
  "node": ">=0.6"
8710
  }
8711
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8712
  "node_modules/totalist": {
8713
  "version": "3.0.0",
8714
  "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
package.json CHANGED
@@ -57,15 +57,14 @@
57
  "@huggingface/hub": "^0.5.1",
58
  "@huggingface/inference": "^2.6.3",
59
  "@iconify-json/bi": "^1.1.21",
60
- "@resvg/resvg-js": "^2.6.2",
61
  "@playwright/browser-chromium": "^1.43.1",
 
62
  "@xenova/transformers": "^2.16.1",
63
  "autoprefixer": "^10.4.14",
64
  "browser-image-resizer": "^2.4.1",
65
  "date-fns": "^2.29.3",
66
  "dotenv": "^16.0.3",
67
  "express": "^4.19.2",
68
- "file-type": "^19.0.0",
69
  "handlebars": "^4.7.8",
70
  "highlight.js": "^11.7.0",
71
  "image-size": "^1.0.2",
@@ -87,7 +86,7 @@
87
  "satori-html": "^0.3.2",
88
  "sbd": "^1.0.19",
89
  "serpapi": "^1.1.1",
90
- "sharp": "^0.33.3",
91
  "tailwind-scrollbar": "^3.0.0",
92
  "tailwindcss": "^3.4.0",
93
  "uuid": "^9.0.1",
@@ -99,6 +98,6 @@
99
  "@google-cloud/vertexai": "^1.1.0",
100
  "aws4fetch": "^1.0.17",
101
  "cohere-ai": "^7.9.0",
102
- "openai": "^4.44.0"
103
  }
104
  }
 
57
  "@huggingface/hub": "^0.5.1",
58
  "@huggingface/inference": "^2.6.3",
59
  "@iconify-json/bi": "^1.1.21",
 
60
  "@playwright/browser-chromium": "^1.43.1",
61
+ "@resvg/resvg-js": "^2.6.0",
62
  "@xenova/transformers": "^2.16.1",
63
  "autoprefixer": "^10.4.14",
64
  "browser-image-resizer": "^2.4.1",
65
  "date-fns": "^2.29.3",
66
  "dotenv": "^16.0.3",
67
  "express": "^4.19.2",
 
68
  "handlebars": "^4.7.8",
69
  "highlight.js": "^11.7.0",
70
  "image-size": "^1.0.2",
 
86
  "satori-html": "^0.3.2",
87
  "sbd": "^1.0.19",
88
  "serpapi": "^1.1.1",
89
+ "sharp": "^0.33.2",
90
  "tailwind-scrollbar": "^3.0.0",
91
  "tailwindcss": "^3.4.0",
92
  "uuid": "^9.0.1",
 
98
  "@google-cloud/vertexai": "^1.1.0",
99
  "aws4fetch": "^1.0.17",
100
  "cohere-ai": "^7.9.0",
101
+ "openai": "^4.14.2"
102
  }
103
  }
src/lib/components/chat/ChatMessage.svelte CHANGED
@@ -308,17 +308,17 @@
308
  {#if message.files && message.files.length > 0}
309
  <div class="mx-auto grid w-fit grid-cols-2 gap-5 px-5">
310
  {#each message.files as file}
311
- <!-- handle the case where this is a hash that points to an image in the db -->
312
- {#if file.type === "hash"}
313
  <img
314
- src={$page.url.pathname + "/output/" + file.value}
315
  alt="input from user"
316
  class="my-2 aspect-auto max-h-48 rounded-lg shadow-lg"
317
  />
318
  {:else}
319
  <!-- handle the case where this is a base64 encoded image -->
320
  <img
321
- src={`data:${file.mime};base64,${file.value}`}
322
  alt="input from user"
323
  class="my-2 aspect-auto max-h-48 rounded-lg shadow-lg"
324
  />
 
308
  {#if message.files && message.files.length > 0}
309
  <div class="mx-auto grid w-fit grid-cols-2 gap-5 px-5">
310
  {#each message.files as file}
311
+ <!-- handle the case where this is a hash that points to an image in the db, hash is always 64 char long -->
312
+ {#if file.length === 64}
313
  <img
314
+ src={$page.url.pathname + "/output/" + file}
315
  alt="input from user"
316
  class="my-2 aspect-auto max-h-48 rounded-lg shadow-lg"
317
  />
318
  {:else}
319
  <!-- handle the case where this is a base64 encoded image -->
320
  <img
321
+ src={"data:image/*;base64," + file}
322
  alt="input from user"
323
  class="my-2 aspect-auto max-h-48 rounded-lg shadow-lg"
324
  />
src/lib/components/chat/ChatWindow.svelte CHANGED
@@ -92,9 +92,7 @@
92
  (lastMessage.from === "user" ||
93
  lastMessage.updates?.findIndex((u) => u.type === "status" && u.status === "error") !== -1);
94
 
95
- $: sources = files?.map((file) =>
96
- file2base64(file).then((value) => ({ type: "base64", value, mime: file.type }))
97
- );
98
 
99
  function onShare() {
100
  dispatch("share");
@@ -231,13 +229,13 @@
231
  <div
232
  class="dark:via-gray-80 pointer-events-none absolute inset-x-0 bottom-0 z-0 mx-auto flex w-full max-w-3xl flex-col items-center justify-center bg-gradient-to-t from-white via-white/80 to-white/0 px-3.5 py-4 max-md:border-t max-md:bg-white sm:px-5 md:py-8 xl:max-w-4xl dark:border-gray-800 dark:from-gray-900 dark:to-gray-900/0 max-md:dark:bg-gray-900 [&>*]:pointer-events-auto"
233
  >
234
- {#if sources?.length}
235
  <div class="flex flex-row flex-wrap justify-center gap-2.5 max-md:pb-3">
236
  {#each sources as source, index}
237
  {#await source then src}
238
  <div class="relative h-16 w-16 overflow-hidden rounded-lg shadow-lg">
239
  <img
240
- src={`data:${src.mime};base64,${src.value}`}
241
  alt="input content"
242
  class="h-full w-full rounded-lg bg-gray-400 object-cover dark:bg-gray-900"
243
  />
 
92
  (lastMessage.from === "user" ||
93
  lastMessage.updates?.findIndex((u) => u.type === "status" && u.status === "error") !== -1);
94
 
95
+ $: sources = files.map((file) => file2base64(file));
 
 
96
 
97
  function onShare() {
98
  dispatch("share");
 
229
  <div
230
  class="dark:via-gray-80 pointer-events-none absolute inset-x-0 bottom-0 z-0 mx-auto flex w-full max-w-3xl flex-col items-center justify-center bg-gradient-to-t from-white via-white/80 to-white/0 px-3.5 py-4 max-md:border-t max-md:bg-white sm:px-5 md:py-8 xl:max-w-4xl dark:border-gray-800 dark:from-gray-900 dark:to-gray-900/0 max-md:dark:bg-gray-900 [&>*]:pointer-events-auto"
231
  >
232
+ {#if sources.length}
233
  <div class="flex flex-row flex-wrap justify-center gap-2.5 max-md:pb-3">
234
  {#each sources as source, index}
235
  {#await source then src}
236
  <div class="relative h-16 w-16 overflow-hidden rounded-lg shadow-lg">
237
  <img
238
+ src={`data:image/*;base64,${src}`}
239
  alt="input content"
240
  class="h-full w-full rounded-lg bg-gray-400 object-cover dark:bg-gray-900"
241
  />
src/lib/server/endpoints/anthropic/endpointAnthropic.ts CHANGED
@@ -1,9 +1,7 @@
1
  import { z } from "zod";
2
- import type { Endpoint } from "../endpoints";
3
  import { env } from "$env/dynamic/private";
 
4
  import type { TextGenerationStreamOutput } from "@huggingface/inference";
5
- import { createImageProcessorOptionsValidator } from "../images";
6
- import { endpointMessagesToAnthropicMessages } from "./utils";
7
 
8
  export const endpointAnthropicParametersSchema = z.object({
9
  weight: z.number().int().positive().default(1),
@@ -13,24 +11,12 @@ export const endpointAnthropicParametersSchema = z.object({
13
  apiKey: z.string().default(env.ANTHROPIC_API_KEY ?? "sk-"),
14
  defaultHeaders: z.record(z.string()).optional(),
15
  defaultQuery: z.record(z.string()).optional(),
16
- multimodal: z
17
- .object({
18
- image: createImageProcessorOptionsValidator({
19
- supportedMimeTypes: ["image/png", "image/jpeg", "image/webp"],
20
- preferredMimeType: "image/webp",
21
- // The 4 / 3 compensates for the 33% increase in size when converting to base64
22
- maxSizeInMB: (5 / 4) * 3,
23
- maxWidth: 4096,
24
- maxHeight: 4096,
25
- }),
26
- })
27
- .default({}),
28
  });
29
 
30
  export async function endpointAnthropic(
31
  input: z.input<typeof endpointAnthropicParametersSchema>
32
  ): Promise<Endpoint> {
33
- const { baseURL, apiKey, model, defaultHeaders, defaultQuery, multimodal } =
34
  endpointAnthropicParametersSchema.parse(input);
35
  let Anthropic;
36
  try {
@@ -52,6 +38,16 @@ export async function endpointAnthropic(
52
  system = messages[0].content;
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
55
  let tokenId = 0;
56
 
57
  const parameters = { ...model.parameters, ...generateSettings };
@@ -59,7 +55,7 @@ export async function endpointAnthropic(
59
  return (async function* () {
60
  const stream = anthropic.messages.stream({
61
  model: model.id ?? model.name,
62
- messages: await endpointMessagesToAnthropicMessages(messages, multimodal),
63
  max_tokens: parameters?.max_new_tokens,
64
  temperature: parameters?.temperature,
65
  top_p: parameters?.top_p,
 
1
  import { z } from "zod";
 
2
  import { env } from "$env/dynamic/private";
3
+ import type { Endpoint } from "../endpoints";
4
  import type { TextGenerationStreamOutput } from "@huggingface/inference";
 
 
5
 
6
  export const endpointAnthropicParametersSchema = z.object({
7
  weight: z.number().int().positive().default(1),
 
11
  apiKey: z.string().default(env.ANTHROPIC_API_KEY ?? "sk-"),
12
  defaultHeaders: z.record(z.string()).optional(),
13
  defaultQuery: z.record(z.string()).optional(),
 
 
 
 
 
 
 
 
 
 
 
 
14
  });
15
 
16
  export async function endpointAnthropic(
17
  input: z.input<typeof endpointAnthropicParametersSchema>
18
  ): Promise<Endpoint> {
19
+ const { baseURL, apiKey, model, defaultHeaders, defaultQuery } =
20
  endpointAnthropicParametersSchema.parse(input);
21
  let Anthropic;
22
  try {
 
38
  system = messages[0].content;
39
  }
40
 
41
+ const messagesFormatted = messages
42
+ .filter((message) => message.from !== "system")
43
+ .map((message) => ({
44
+ role: message.from,
45
+ content: message.content,
46
+ })) as unknown as {
47
+ role: "user" | "assistant";
48
+ content: string;
49
+ }[];
50
+
51
  let tokenId = 0;
52
 
53
  const parameters = { ...model.parameters, ...generateSettings };
 
55
  return (async function* () {
56
  const stream = anthropic.messages.stream({
57
  model: model.id ?? model.name,
58
+ messages: messagesFormatted,
59
  max_tokens: parameters?.max_new_tokens,
60
  temperature: parameters?.temperature,
61
  top_p: parameters?.top_p,
src/lib/server/endpoints/anthropic/endpointAnthropicVertex.ts CHANGED
@@ -1,8 +1,6 @@
1
  import { z } from "zod";
2
  import type { Endpoint } from "../endpoints";
3
  import type { TextGenerationStreamOutput } from "@huggingface/inference";
4
- import { createImageProcessorOptionsValidator } from "../images";
5
- import { endpointMessagesToAnthropicMessages } from "./utils";
6
 
7
  export const endpointAnthropicVertexParametersSchema = z.object({
8
  weight: z.number().int().positive().default(1),
@@ -12,24 +10,12 @@ export const endpointAnthropicVertexParametersSchema = z.object({
12
  projectId: z.string(),
13
  defaultHeaders: z.record(z.string()).optional(),
14
  defaultQuery: z.record(z.string()).optional(),
15
- multimodal: z
16
- .object({
17
- image: createImageProcessorOptionsValidator({
18
- supportedMimeTypes: ["image/png", "image/jpeg", "image/webp"],
19
- preferredMimeType: "image/webp",
20
- // The 4 / 3 compensates for the 33% increase in size when converting to base64
21
- maxSizeInMB: (5 / 4) * 3,
22
- maxWidth: 4096,
23
- maxHeight: 4096,
24
- }),
25
- })
26
- .default({}),
27
  });
28
 
29
  export async function endpointAnthropicVertex(
30
  input: z.input<typeof endpointAnthropicVertexParametersSchema>
31
  ): Promise<Endpoint> {
32
- const { region, projectId, model, defaultHeaders, defaultQuery, multimodal } =
33
  endpointAnthropicVertexParametersSchema.parse(input);
34
  let AnthropicVertex;
35
  try {
@@ -52,11 +38,21 @@ export async function endpointAnthropicVertex(
52
  system = messages[0].content;
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
55
  let tokenId = 0;
56
  return (async function* () {
57
  const stream = anthropic.messages.stream({
58
  model: model.id ?? model.name,
59
- messages: await endpointMessagesToAnthropicMessages(messages, multimodal),
60
  max_tokens: model.parameters?.max_new_tokens,
61
  temperature: model.parameters?.temperature,
62
  top_p: model.parameters?.top_p,
 
1
  import { z } from "zod";
2
  import type { Endpoint } from "../endpoints";
3
  import type { TextGenerationStreamOutput } from "@huggingface/inference";
 
 
4
 
5
  export const endpointAnthropicVertexParametersSchema = z.object({
6
  weight: z.number().int().positive().default(1),
 
10
  projectId: z.string(),
11
  defaultHeaders: z.record(z.string()).optional(),
12
  defaultQuery: z.record(z.string()).optional(),
 
 
 
 
 
 
 
 
 
 
 
 
13
  });
14
 
15
  export async function endpointAnthropicVertex(
16
  input: z.input<typeof endpointAnthropicVertexParametersSchema>
17
  ): Promise<Endpoint> {
18
+ const { region, projectId, model, defaultHeaders, defaultQuery } =
19
  endpointAnthropicVertexParametersSchema.parse(input);
20
  let AnthropicVertex;
21
  try {
 
38
  system = messages[0].content;
39
  }
40
 
41
+ const messagesFormatted = messages
42
+ .filter((message) => message.from !== "system")
43
+ .map((message) => ({
44
+ role: message.from,
45
+ content: message.content,
46
+ })) as unknown as {
47
+ role: "user" | "assistant";
48
+ content: string;
49
+ }[];
50
+
51
  let tokenId = 0;
52
  return (async function* () {
53
  const stream = anthropic.messages.stream({
54
  model: model.id ?? model.name,
55
+ messages: messagesFormatted,
56
  max_tokens: model.parameters?.max_new_tokens,
57
  temperature: model.parameters?.temperature,
58
  top_p: model.parameters?.top_p,
src/lib/server/endpoints/anthropic/utils.ts DELETED
@@ -1,44 +0,0 @@
1
- import type { ImageBlockParam, MessageParam } from "@anthropic-ai/sdk/resources";
2
- import { makeImageProcessor, type ImageProcessorOptions } from "../images";
3
- import type { EndpointMessage } from "../endpoints";
4
- import type { MessageFile } from "$lib/types/Message";
5
-
6
- export async function fileToImageBlock(
7
- file: MessageFile,
8
- opts: ImageProcessorOptions<"image/png" | "image/jpeg" | "image/webp">
9
- ): Promise<ImageBlockParam> {
10
- const processor = makeImageProcessor(opts);
11
- const { image, mime } = await processor(file);
12
-
13
- return {
14
- type: "image",
15
- source: {
16
- type: "base64",
17
- media_type: mime,
18
- data: image.toString("base64"),
19
- },
20
- };
21
- }
22
-
23
- type NonSystemMessage = EndpointMessage & { from: "user" | "assistant" };
24
-
25
- export async function endpointMessagesToAnthropicMessages(
26
- messages: EndpointMessage[],
27
- multimodal: { image: ImageProcessorOptions<"image/png" | "image/jpeg" | "image/webp"> }
28
- ): Promise<MessageParam[]> {
29
- return await Promise.all(
30
- messages
31
- .filter((message): message is NonSystemMessage => message.from !== "system")
32
- .map<Promise<MessageParam>>(async (message) => {
33
- return {
34
- role: message.from,
35
- content: [
36
- ...(await Promise.all(
37
- (message.files ?? []).map((file) => fileToImageBlock(file, multimodal.image))
38
- )),
39
- { type: "text", text: message.content },
40
- ],
41
- };
42
- })
43
- );
44
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/server/endpoints/endpoints.ts CHANGED
@@ -1,5 +1,4 @@
1
  import type { Conversation } from "$lib/types/Conversation";
2
- import type { Message } from "$lib/types/Message";
3
  import type { TextGenerationStreamOutput } from "@huggingface/inference";
4
  import { endpointTgi, endpointTgiParametersSchema } from "./tgi/endpointTgi";
5
  import { z } from "zod";
@@ -26,10 +25,9 @@ import endpointLangserve, {
26
  endpointLangserveParametersSchema,
27
  } from "./langserve/endpointLangserve";
28
 
29
- export type EndpointMessage = Omit<Message, "id">;
30
  // parameters passed when generating text
31
  export interface EndpointParameters {
32
- messages: EndpointMessage[];
33
  preprompt?: Conversation["preprompt"];
34
  continueMessage?: boolean; // used to signal that the last message will be extended
35
  generateSettings?: Partial<Model["parameters"]>;
 
1
  import type { Conversation } from "$lib/types/Conversation";
 
2
  import type { TextGenerationStreamOutput } from "@huggingface/inference";
3
  import { endpointTgi, endpointTgiParametersSchema } from "./tgi/endpointTgi";
4
  import { z } from "zod";
 
25
  endpointLangserveParametersSchema,
26
  } from "./langserve/endpointLangserve";
27
 
 
28
  // parameters passed when generating text
29
  export interface EndpointParameters {
30
+ messages: Omit<Conversation["messages"][0], "id">[];
31
  preprompt?: Conversation["preprompt"];
32
  continueMessage?: boolean; // used to signal that the last message will be extended
33
  generateSettings?: Partial<Model["parameters"]>;
src/lib/server/endpoints/images.ts DELETED
@@ -1,211 +0,0 @@
1
- import type { Sharp } from "sharp";
2
- import sharp from "sharp";
3
- import type { MessageFile } from "$lib/types/Message";
4
- import { z, type util } from "zod";
5
-
6
- export interface ImageProcessorOptions<TMimeType extends string = string> {
7
- supportedMimeTypes: TMimeType[];
8
- preferredMimeType: TMimeType;
9
- maxSizeInMB: number;
10
- maxWidth: number;
11
- maxHeight: number;
12
- }
13
- export type ImageProcessor<TMimeType extends string = string> = (file: MessageFile) => Promise<{
14
- image: Buffer;
15
- mime: TMimeType;
16
- }>;
17
-
18
- export function createImageProcessorOptionsValidator<TMimeType extends string = string>(
19
- defaults: ImageProcessorOptions<TMimeType>
20
- ) {
21
- return z
22
- .object({
23
- supportedMimeTypes: z
24
- .array(
25
- z.enum<string, [TMimeType, ...TMimeType[]]>([
26
- defaults.supportedMimeTypes[0],
27
- ...defaults.supportedMimeTypes.slice(1),
28
- ])
29
- )
30
- .default(defaults.supportedMimeTypes),
31
- preferredMimeType: z
32
- .enum([defaults.supportedMimeTypes[0], ...defaults.supportedMimeTypes.slice(1)])
33
- .default(defaults.preferredMimeType as util.noUndefined<TMimeType>),
34
- maxSizeInMB: z.number().positive().default(defaults.maxSizeInMB),
35
- maxWidth: z.number().int().positive().default(defaults.maxWidth),
36
- maxHeight: z.number().int().positive().default(defaults.maxHeight),
37
- })
38
- .default(defaults);
39
- }
40
-
41
- export function makeImageProcessor<TMimeType extends string = string>(
42
- options: ImageProcessorOptions<TMimeType>
43
- ): ImageProcessor<TMimeType> {
44
- return async (file) => {
45
- const { supportedMimeTypes, preferredMimeType, maxSizeInMB, maxWidth, maxHeight } = options;
46
- const { mime, value } = file;
47
-
48
- const buffer = Buffer.from(value, "base64");
49
- let sharpInst = sharp(buffer);
50
-
51
- const metadata = await sharpInst.metadata();
52
- if (!metadata) throw Error("Failed to read image metadata");
53
- const { width, height } = metadata;
54
- if (width === undefined || height === undefined) throw Error("Failed to read image size");
55
-
56
- const tooLargeInSize = width > maxWidth || height > maxHeight;
57
- const tooLargeInBytes = buffer.byteLength > maxSizeInMB * 1000 * 1000;
58
-
59
- const outputMime = chooseMimeType(supportedMimeTypes, preferredMimeType, mime, {
60
- preferSizeReduction: tooLargeInBytes,
61
- });
62
-
63
- // Resize if necessary
64
- if (tooLargeInSize || tooLargeInBytes) {
65
- const size = chooseImageSize({
66
- mime: outputMime,
67
- width,
68
- height,
69
- maxWidth,
70
- maxHeight,
71
- maxSizeInMB,
72
- });
73
- if (size.width !== width || size.height !== height) {
74
- sharpInst = resizeImage(sharpInst, size.width, size.height);
75
- }
76
- }
77
-
78
- // Convert format if necessary
79
- // We always want to convert the image when the file was too large in bytes
80
- // so we can guarantee that ideal options are used, which are expected when
81
- // choosing the image size
82
- if (outputMime !== mime || tooLargeInBytes) {
83
- sharpInst = convertImage(sharpInst, outputMime);
84
- }
85
-
86
- const processedImage = await sharpInst.toBuffer();
87
- return { image: processedImage, mime: outputMime };
88
- };
89
- }
90
-
91
- const outputFormats = ["png", "jpeg", "webp", "avif", "tiff", "gif"] as const;
92
- type OutputImgFormat = (typeof outputFormats)[number];
93
- const isOutputFormat = (format: string): format is (typeof outputFormats)[number] =>
94
- outputFormats.includes(format as OutputImgFormat);
95
-
96
- export function convertImage(sharpInst: Sharp, outputMime: string): Sharp {
97
- const [type, format] = outputMime.split("/");
98
- if (type !== "image") throw Error(`Requested non-image mime type: ${outputMime}`);
99
- if (!isOutputFormat(format)) {
100
- throw Error(`Requested to convert to an unsupported format: ${format}`);
101
- }
102
-
103
- return sharpInst[format]();
104
- }
105
-
106
- // heic/heif requires proprietary license
107
- // TODO: blocking heif may be incorrect considering it also supports av1, so we should instead
108
- // detect the compression method used via sharp().metadata().compression
109
- // TODO: consider what to do about animated formats: apng, gif, animated webp, ...
110
- const blocklistedMimes = ["image/heic", "image/heif"];
111
-
112
- /** Sorted from largest to smallest */
113
- const mimesBySizeDesc = [
114
- "image/png",
115
- "image/tiff",
116
- "image/gif",
117
- "image/jpeg",
118
- "image/webp",
119
- "image/avif",
120
- ];
121
-
122
- /**
123
- * Defaults to preferred format or uses existing mime if supported
124
- * When preferSizeReduction is true, it will choose the smallest format that is supported
125
- **/
126
- function chooseMimeType<T extends readonly string[]>(
127
- supportedMimes: T,
128
- preferredMime: string,
129
- mime: string,
130
- { preferSizeReduction }: { preferSizeReduction: boolean }
131
- ): T[number] {
132
- if (!supportedMimes.includes(preferredMime)) {
133
- const supportedMimesStr = supportedMimes.join(", ");
134
- throw Error(
135
- `Preferred format "${preferredMime}" not found in supported mimes: ${supportedMimesStr}`
136
- );
137
- }
138
-
139
- const [type] = mime.split("/");
140
- if (type !== "image") throw Error(`Received non-image mime type: ${mime}`);
141
-
142
- if (supportedMimes.includes(mime) && !preferSizeReduction) return mime;
143
-
144
- if (blocklistedMimes.includes(mime)) throw Error(`Received blocklisted mime type: ${mime}`);
145
-
146
- const smallestMime = mimesBySizeDesc.findLast((m) => supportedMimes.includes(m));
147
- return smallestMime ?? preferredMime;
148
- }
149
-
150
- interface ImageSizeOptions {
151
- mime: string;
152
- width: number;
153
- height: number;
154
- maxWidth: number;
155
- maxHeight: number;
156
- maxSizeInMB: number;
157
- }
158
-
159
- /** Resizes the image to fit within the specified size in MB by guessing the output size */
160
- export function chooseImageSize({
161
- mime,
162
- width,
163
- height,
164
- maxWidth,
165
- maxHeight,
166
- maxSizeInMB,
167
- }: ImageSizeOptions): { width: number; height: number } {
168
- const biggestDiscrepency = Math.max(1, width / maxWidth, height / maxHeight);
169
-
170
- let selectedWidth = Math.ceil(width / biggestDiscrepency);
171
- let selectedHeight = Math.ceil(height / biggestDiscrepency);
172
-
173
- do {
174
- const estimatedSize = estimateImageSizeInBytes(mime, selectedWidth, selectedHeight);
175
- if (estimatedSize < maxSizeInMB * 1024 * 1024) {
176
- return { width: selectedWidth, height: selectedHeight };
177
- }
178
- selectedWidth = Math.floor(selectedWidth / 1.1);
179
- selectedHeight = Math.floor(selectedHeight / 1.1);
180
- } while (selectedWidth > 1 && selectedHeight > 1);
181
-
182
- throw Error(`Failed to resize image to fit within ${maxSizeInMB}MB`);
183
- }
184
-
185
- const mimeToCompressionRatio: Record<string, number> = {
186
- "image/png": 1 / 2,
187
- "image/jpeg": 1 / 10,
188
- "image/webp": 1 / 4,
189
- "image/avif": 1 / 5,
190
- "image/tiff": 1,
191
- "image/gif": 1 / 5,
192
- };
193
-
194
- /**
195
- * Guesses the side of an image in MB based on its format and dimensions
196
- * Should guess the worst case
197
- **/
198
- function estimateImageSizeInBytes(mime: string, width: number, height: number): number {
199
- const compressionRatio = mimeToCompressionRatio[mime];
200
- if (!compressionRatio) throw Error(`Unsupported image format: ${mime}`);
201
-
202
- const bitsPerPixel = 32; // Assuming 32-bit color depth for 8-bit R G B A
203
- const bytesPerPixel = bitsPerPixel / 8;
204
- const uncompressedSize = width * height * bytesPerPixel;
205
-
206
- return uncompressedSize * compressionRatio;
207
- }
208
-
209
- export function resizeImage(sharpInst: Sharp, maxWidth: number, maxHeight: number): Sharp {
210
- return sharpInst.resize({ width: maxWidth, height: maxHeight, fit: "inside" });
211
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/server/endpoints/openai/endpointOai.ts CHANGED
@@ -6,10 +6,6 @@ import type { ChatCompletionCreateParamsStreaming } from "openai/resources/chat/
6
  import { buildPrompt } from "$lib/buildPrompt";
7
  import { env } from "$env/dynamic/private";
8
  import type { Endpoint } from "../endpoints";
9
- import type OpenAI from "openai";
10
- import { createImageProcessorOptionsValidator, makeImageProcessor } from "../images";
11
- import type { MessageFile } from "$lib/types/Message";
12
- import type { EndpointMessage } from "../endpoints";
13
 
14
  export const endpointOAIParametersSchema = z.object({
15
  weight: z.number().int().positive().default(1),
@@ -23,41 +19,13 @@ export const endpointOAIParametersSchema = z.object({
23
  defaultHeaders: z.record(z.string()).optional(),
24
  defaultQuery: z.record(z.string()).optional(),
25
  extraBody: z.record(z.any()).optional(),
26
- multimodal: z
27
- .object({
28
- image: createImageProcessorOptionsValidator({
29
- supportedMimeTypes: [
30
- "image/png",
31
- "image/jpeg",
32
- "image/webp",
33
- "image/avif",
34
- "image/tiff",
35
- "image/gif",
36
- ],
37
- preferredMimeType: "image/webp",
38
- maxSizeInMB: Infinity,
39
- maxWidth: 4096,
40
- maxHeight: 4096,
41
- }),
42
- })
43
- .default({}),
44
  });
45
 
46
  export async function endpointOai(
47
  input: z.input<typeof endpointOAIParametersSchema>
48
  ): Promise<Endpoint> {
49
- const {
50
- baseURL,
51
- apiKey,
52
- completion,
53
- model,
54
- defaultHeaders,
55
- defaultQuery,
56
- multimodal,
57
- extraBody,
58
- } = endpointOAIParametersSchema.parse(input);
59
-
60
- /* eslint-disable-next-line no-shadow */
61
  let OpenAI;
62
  try {
63
  OpenAI = (await import("openai")).OpenAI;
@@ -72,8 +40,6 @@ export async function endpointOai(
72
  defaultQuery,
73
  });
74
 
75
- const imageProcessor = makeImageProcessor(multimodal.image);
76
-
77
  if (completion === "completions") {
78
  return async ({ messages, preprompt, continueMessage, generateSettings }) => {
79
  const prompt = await buildPrompt({
@@ -103,8 +69,10 @@ export async function endpointOai(
103
  };
104
  } else if (completion === "chat_completions") {
105
  return async ({ messages, preprompt, generateSettings }) => {
106
- let messagesOpenAI: OpenAI.Chat.Completions.ChatCompletionMessageParam[] =
107
- await prepareMessages(messages, imageProcessor);
 
 
108
 
109
  if (messagesOpenAI?.[0]?.role !== "system") {
110
  messagesOpenAI = [{ role: "system", content: "" }, ...messagesOpenAI];
@@ -136,39 +104,3 @@ export async function endpointOai(
136
  throw new Error("Invalid completion type");
137
  }
138
  }
139
-
140
- async function prepareMessages(
141
- messages: EndpointMessage[],
142
- imageProcessor: ReturnType<typeof makeImageProcessor>
143
- ): Promise<OpenAI.Chat.Completions.ChatCompletionMessageParam[]> {
144
- return Promise.all(
145
- messages.map(async (message) => {
146
- if (message.from === "user") {
147
- return {
148
- role: message.from,
149
- content: [
150
- ...(await prepareFiles(imageProcessor, message.files ?? [])),
151
- { type: "text", text: message.content },
152
- ],
153
- };
154
- }
155
- return {
156
- role: message.from,
157
- content: message.content,
158
- };
159
- })
160
- );
161
- }
162
-
163
- async function prepareFiles(
164
- imageProcessor: ReturnType<typeof makeImageProcessor>,
165
- files: MessageFile[]
166
- ): Promise<OpenAI.Chat.Completions.ChatCompletionContentPartImage[]> {
167
- const processedFiles = await Promise.all(files.map(imageProcessor));
168
- return processedFiles.map((file) => ({
169
- type: "image_url" as const,
170
- image_url: {
171
- url: `data:${file.mime};base64,${file.image.toString("base64")}`,
172
- },
173
- }));
174
- }
 
6
  import { buildPrompt } from "$lib/buildPrompt";
7
  import { env } from "$env/dynamic/private";
8
  import type { Endpoint } from "../endpoints";
 
 
 
 
9
 
10
  export const endpointOAIParametersSchema = z.object({
11
  weight: z.number().int().positive().default(1),
 
19
  defaultHeaders: z.record(z.string()).optional(),
20
  defaultQuery: z.record(z.string()).optional(),
21
  extraBody: z.record(z.any()).optional(),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  });
23
 
24
  export async function endpointOai(
25
  input: z.input<typeof endpointOAIParametersSchema>
26
  ): Promise<Endpoint> {
27
+ const { baseURL, apiKey, completion, model, defaultHeaders, defaultQuery, extraBody } =
28
+ endpointOAIParametersSchema.parse(input);
 
 
 
 
 
 
 
 
 
 
29
  let OpenAI;
30
  try {
31
  OpenAI = (await import("openai")).OpenAI;
 
40
  defaultQuery,
41
  });
42
 
 
 
43
  if (completion === "completions") {
44
  return async ({ messages, preprompt, continueMessage, generateSettings }) => {
45
  const prompt = await buildPrompt({
 
69
  };
70
  } else if (completion === "chat_completions") {
71
  return async ({ messages, preprompt, generateSettings }) => {
72
+ let messagesOpenAI = messages.map((message) => ({
73
+ role: message.from,
74
+ content: message.content,
75
+ }));
76
 
77
  if (messagesOpenAI?.[0]?.role !== "system") {
78
  messagesOpenAI = [{ role: "system", content: "" }, ...messagesOpenAI];
 
104
  throw new Error("Invalid completion type");
105
  }
106
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/server/endpoints/preprocessMessages.ts DELETED
@@ -1,56 +0,0 @@
1
- import type { Message } from "$lib/types/Message";
2
- import { format } from "date-fns";
3
- import type { EndpointMessage } from "./endpoints";
4
- import { downloadFile } from "../files/downloadFile";
5
- import type { ObjectId } from "mongodb";
6
-
7
- export async function preprocessMessages(
8
- messages: Message[],
9
- webSearch: Message["webSearch"],
10
- convId: ObjectId
11
- ): Promise<EndpointMessage[]> {
12
- return Promise.resolve(messages)
13
- .then((msgs) => addWebSearchContext(msgs, webSearch))
14
- .then((msgs) => downloadFiles(msgs, convId));
15
- }
16
-
17
- function addWebSearchContext(messages: Message[], webSearch: Message["webSearch"]) {
18
- const webSearchContext = webSearch?.contextSources
19
- .map(({ context }) => context.trim())
20
- .join("\n\n----------\n\n");
21
-
22
- // No web search context available, skip
23
- if (!webSearch || !webSearchContext?.trim()) return messages;
24
- // No messages available, skip
25
- if (messages.length === 0) return messages;
26
-
27
- const lastQuestion = messages.findLast((el) => el.from === "user")?.content ?? "";
28
- const previousQuestions = messages
29
- .filter((el) => el.from === "user")
30
- .slice(0, -1)
31
- .map((el) => el.content);
32
- const currentDate = format(new Date(), "MMMM d, yyyy");
33
-
34
- const finalMessage = {
35
- ...messages[messages.length - 1],
36
- content: `I searched the web using the query: ${webSearch.searchQuery}.
37
- Today is ${currentDate} and here are the results:
38
- =====================
39
- ${webSearchContext}
40
- =====================
41
- ${previousQuestions.length > 0 ? `Previous questions: \n- ${previousQuestions.join("\n- ")}` : ""}
42
- Answer the question: ${lastQuestion}`,
43
- };
44
-
45
- return [...messages.slice(0, -1), finalMessage];
46
- }
47
-
48
- async function downloadFiles(messages: Message[], convId: ObjectId): Promise<EndpointMessage[]> {
49
- return Promise.all(
50
- messages.map<Promise<EndpointMessage>>((message) =>
51
- Promise.all((message.files ?? []).map((file) => downloadFile(file.value, convId))).then(
52
- (files) => ({ ...message, files })
53
- )
54
- )
55
- );
56
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/server/endpoints/tgi/endpointTgi.ts CHANGED
@@ -1,13 +1,8 @@
1
  import { env } from "$env/dynamic/private";
2
  import { buildPrompt } from "$lib/buildPrompt";
3
  import { textGenerationStream } from "@huggingface/inference";
4
- import type { Endpoint, EndpointMessage } from "../endpoints";
5
  import { z } from "zod";
6
- import {
7
- createImageProcessorOptionsValidator,
8
- makeImageProcessor,
9
- type ImageProcessor,
10
- } from "../images";
11
 
12
  export const endpointTgiParametersSchema = z.object({
13
  weight: z.number().int().positive().default(1),
@@ -16,32 +11,14 @@ export const endpointTgiParametersSchema = z.object({
16
  url: z.string().url(),
17
  accessToken: z.string().default(env.HF_TOKEN ?? env.HF_ACCESS_TOKEN),
18
  authorization: z.string().optional(),
19
- multimodal: z
20
- .object({
21
- // Assumes IDEFICS
22
- image: createImageProcessorOptionsValidator({
23
- supportedMimeTypes: ["image/jpeg", "image/webp"],
24
- preferredMimeType: "image/webp",
25
- maxSizeInMB: 5,
26
- maxWidth: 224,
27
- maxHeight: 224,
28
- }),
29
- })
30
- .default({}),
31
  });
32
 
33
  export function endpointTgi(input: z.input<typeof endpointTgiParametersSchema>): Endpoint {
34
- const { url, accessToken, model, authorization, multimodal } =
35
- endpointTgiParametersSchema.parse(input);
36
- const imageProcessor = makeImageProcessor(multimodal.image);
37
 
38
  return async ({ messages, preprompt, continueMessage, generateSettings }) => {
39
- const messagesWithResizedFiles = await Promise.all(
40
- messages.map((message) => prepareMessage(message, imageProcessor))
41
- );
42
-
43
  const prompt = await buildPrompt({
44
- messages: messagesWithResizedFiles,
45
  preprompt,
46
  model,
47
  continueMessage,
@@ -71,23 +48,4 @@ export function endpointTgi(input: z.input<typeof endpointTgiParametersSchema>):
71
  };
72
  }
73
 
74
- const whiteImage = {
75
- mime: "image/png",
76
- image: Buffer.from(
77
- "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAAQABADAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/igAoAKACgD/2Q==",
78
- "base64"
79
- ),
80
- };
81
-
82
- async function prepareMessage(
83
- message: EndpointMessage,
84
- imageProcessor: ImageProcessor
85
- ): Promise<EndpointMessage> {
86
- const files = await Promise.all(message.files?.map(imageProcessor) ?? [whiteImage]);
87
- const markdowns = files.map(
88
- (file) => `![](data:${file.mime};base64,${file.image.toString("base64")})`
89
- );
90
- const content = message.content + "\n" + markdowns.join("\n ");
91
-
92
- return { ...message, content };
93
- }
 
1
  import { env } from "$env/dynamic/private";
2
  import { buildPrompt } from "$lib/buildPrompt";
3
  import { textGenerationStream } from "@huggingface/inference";
4
+ import type { Endpoint } from "../endpoints";
5
  import { z } from "zod";
 
 
 
 
 
6
 
7
  export const endpointTgiParametersSchema = z.object({
8
  weight: z.number().int().positive().default(1),
 
11
  url: z.string().url(),
12
  accessToken: z.string().default(env.HF_TOKEN ?? env.HF_ACCESS_TOKEN),
13
  authorization: z.string().optional(),
 
 
 
 
 
 
 
 
 
 
 
 
14
  });
15
 
16
  export function endpointTgi(input: z.input<typeof endpointTgiParametersSchema>): Endpoint {
17
+ const { url, accessToken, model, authorization } = endpointTgiParametersSchema.parse(input);
 
 
18
 
19
  return async ({ messages, preprompt, continueMessage, generateSettings }) => {
 
 
 
 
20
  const prompt = await buildPrompt({
21
+ messages,
22
  preprompt,
23
  model,
24
  continueMessage,
 
48
  };
49
  }
50
 
51
+ export default endpointTgi;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/server/files/downloadFile.ts CHANGED
@@ -2,16 +2,15 @@ import { error } from "@sveltejs/kit";
2
  import { collections } from "$lib/server/database";
3
  import type { Conversation } from "$lib/types/Conversation";
4
  import type { SharedConversation } from "$lib/types/SharedConversation";
5
- import type { MessageFile } from "$lib/types/Message";
6
 
7
  export async function downloadFile(
8
  sha256: string,
9
  convId: Conversation["_id"] | SharedConversation["_id"]
10
- ): Promise<MessageFile & { type: "base64" }> {
11
  const fileId = collections.bucket.find({ filename: `${convId.toString()}-${sha256}` });
12
  let mime = "";
13
 
14
- const buffer = await fileId.next().then(async (file) => {
15
  if (!file) {
16
  throw error(404, "File not found");
17
  }
@@ -33,5 +32,5 @@ export async function downloadFile(
33
  return fileBuffer;
34
  });
35
 
36
- return { type: "base64", value: buffer.toString("base64"), mime };
37
  }
 
2
  import { collections } from "$lib/server/database";
3
  import type { Conversation } from "$lib/types/Conversation";
4
  import type { SharedConversation } from "$lib/types/SharedConversation";
 
5
 
6
  export async function downloadFile(
7
  sha256: string,
8
  convId: Conversation["_id"] | SharedConversation["_id"]
9
+ ) {
10
  const fileId = collections.bucket.find({ filename: `${convId.toString()}-${sha256}` });
11
  let mime = "";
12
 
13
+ const content = await fileId.next().then(async (file) => {
14
  if (!file) {
15
  throw error(404, "File not found");
16
  }
 
32
  return fileBuffer;
33
  });
34
 
35
+ return { content, mime };
36
  }
src/lib/server/files/uploadFile.ts CHANGED
@@ -1,27 +1,21 @@
1
  import type { Conversation } from "$lib/types/Conversation";
2
- import type { MessageFile } from "$lib/types/Message";
3
  import { sha256 } from "$lib/utils/sha256";
4
- import { fileTypeFromBuffer } from "file-type";
5
  import { collections } from "$lib/server/database";
6
 
7
- export async function uploadFile(file: File, conv: Conversation): Promise<MessageFile> {
8
  const sha = await sha256(await file.text());
9
- const buffer = await file.arrayBuffer();
10
-
11
- // Attempt to detect the mime type of the file, fallback to the uploaded mime
12
- const mime = await fileTypeFromBuffer(buffer).then((fileType) => fileType?.mime ?? file.type);
13
 
14
  const upload = collections.bucket.openUploadStream(`${conv._id}-${sha}`, {
15
- metadata: { conversation: conv._id.toString(), mime },
16
  });
17
 
18
  upload.write((await file.arrayBuffer()) as unknown as Buffer);
19
  upload.end();
20
 
21
- // only return the filename when upload throws a finish event or a 20s time out occurs
22
  return new Promise((resolve, reject) => {
23
- upload.once("finish", () => resolve({ type: "hash", value: sha, mime: file.type }));
24
  upload.once("error", reject);
25
- setTimeout(() => reject(new Error("Upload timed out")), 20_000);
26
  });
27
  }
 
1
  import type { Conversation } from "$lib/types/Conversation";
 
2
  import { sha256 } from "$lib/utils/sha256";
 
3
  import { collections } from "$lib/server/database";
4
 
5
+ export async function uploadFile(file: Blob, conv: Conversation): Promise<string> {
6
  const sha = await sha256(await file.text());
 
 
 
 
7
 
8
  const upload = collections.bucket.openUploadStream(`${conv._id}-${sha}`, {
9
+ metadata: { conversation: conv._id.toString(), mime: "image/jpeg" },
10
  });
11
 
12
  upload.write((await file.arrayBuffer()) as unknown as Buffer);
13
  upload.end();
14
 
15
+ // only return the filename when upload throws a finish event or a 10s time out occurs
16
  return new Promise((resolve, reject) => {
17
+ upload.once("finish", () => resolve(sha));
18
  upload.once("error", reject);
19
+ setTimeout(() => reject(new Error("Upload timed out")), 10000);
20
  });
21
  }
src/lib/server/generateFromDefaultEndpoint.ts CHANGED
@@ -1,12 +1,12 @@
1
  import { smallModel } from "$lib/server/models";
2
- import type { EndpointMessage } from "./endpoints/endpoints";
3
 
4
  export async function generateFromDefaultEndpoint({
5
  messages,
6
  preprompt,
7
  generateSettings,
8
  }: {
9
- messages: EndpointMessage[];
10
  preprompt?: string;
11
  generateSettings?: Record<string, unknown>;
12
  }): Promise<string> {
 
1
  import { smallModel } from "$lib/server/models";
2
+ import type { Conversation } from "$lib/types/Conversation";
3
 
4
  export async function generateFromDefaultEndpoint({
5
  messages,
6
  preprompt,
7
  generateSettings,
8
  }: {
9
+ messages: Omit<Conversation["messages"][0], "id">[];
10
  preprompt?: string;
11
  generateSettings?: Record<string, unknown>;
12
  }): Promise<string> {
src/lib/server/models.ts CHANGED
@@ -3,7 +3,7 @@ import type { ChatTemplateInput } from "$lib/types/Template";
3
  import { compileTemplate } from "$lib/utils/template";
4
  import { z } from "zod";
5
  import endpoints, { endpointSchema, type Endpoint } from "./endpoints/endpoints";
6
- import { endpointTgi } from "./endpoints/tgi/endpointTgi";
7
  import { sum } from "$lib/utils/sum";
8
  import { embeddingModels, validateEmbeddingModelByName } from "./embeddingModels";
9
 
 
3
  import { compileTemplate } from "$lib/utils/template";
4
  import { z } from "zod";
5
  import endpoints, { endpointSchema, type Endpoint } from "./endpoints/endpoints";
6
+ import endpointTgi from "./endpoints/tgi/endpointTgi";
7
  import { sum } from "$lib/utils/sum";
8
  import { embeddingModels, validateEmbeddingModelByName } from "./embeddingModels";
9
 
src/lib/server/preprocessMessages.ts ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Conversation } from "$lib/types/Conversation";
2
+ import type { Message } from "$lib/types/Message";
3
+ import { format } from "date-fns";
4
+ import { downloadFile } from "./files/downloadFile";
5
+ import { logger } from "$lib/server/logger";
6
+
7
+ export async function preprocessMessages(
8
+ messages: Message[],
9
+ webSearch: Message["webSearch"],
10
+ multimodal: boolean,
11
+ id: Conversation["_id"]
12
+ ): Promise<Message[]> {
13
+ return await Promise.all(
14
+ structuredClone(messages).map(async (message, idx) => {
15
+ const webSearchContext = webSearch?.contextSources
16
+ .map(({ context }) => context.trim())
17
+ .join("\n\n----------\n\n");
18
+
19
+ // start by adding websearch to the last message
20
+ if (idx === messages.length - 1 && webSearch && webSearchContext?.trim()) {
21
+ const lastQuestion = messages.findLast((el) => el.from === "user")?.content ?? "";
22
+ const previousQuestions = messages
23
+ .filter((el) => el.from === "user")
24
+ .slice(0, -1)
25
+ .map((el) => el.content);
26
+ const currentDate = format(new Date(), "MMMM d, yyyy");
27
+
28
+ message.content = `I searched the web using the query: ${webSearch.searchQuery}.
29
+ Today is ${currentDate} and here are the results:
30
+ =====================
31
+ ${webSearchContext}
32
+ =====================
33
+ ${previousQuestions.length > 0 ? `Previous questions: \n- ${previousQuestions.join("\n- ")}` : ""}
34
+ Answer the question: ${lastQuestion}`;
35
+ }
36
+ // handle files if model is multimodal
37
+ if (multimodal) {
38
+ if (message.files && message.files.length > 0) {
39
+ const markdowns = await Promise.all(
40
+ message.files.map(async (hash) => {
41
+ try {
42
+ const { content: image, mime } = await downloadFile(hash, id);
43
+ const b64 = image.toString("base64");
44
+ return `![](data:${mime};base64,${b64})})`;
45
+ } catch (e) {
46
+ logger.error(e);
47
+ }
48
+ })
49
+ );
50
+ message.content += markdowns.join("\n ");
51
+ } else {
52
+ // if no image, append an empty white image
53
+ message.content +=
54
+ "\n![](data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAAQABADAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/igAoAKACgD/2Q==)";
55
+ }
56
+ }
57
+
58
+ return message;
59
+ })
60
+ );
61
+ }
src/lib/server/summarize.ts CHANGED
@@ -1,6 +1,6 @@
1
  import { env } from "$env/dynamic/private";
2
  import { generateFromDefaultEndpoint } from "$lib/server/generateFromDefaultEndpoint";
3
- import type { EndpointMessage } from "./endpoints/endpoints";
4
  import { logger } from "$lib/server/logger";
5
 
6
  export async function summarize(prompt: string) {
@@ -8,7 +8,7 @@ export async function summarize(prompt: string) {
8
  return prompt.split(/\s+/g).slice(0, 5).join(" ");
9
  }
10
 
11
- const messages: Array<EndpointMessage> = [
12
  { from: "user", content: "Who is the president of Gabon?" },
13
  { from: "assistant", content: "🇬🇦 President of Gabon" },
14
  { from: "user", content: "Who is Julien Chaumond?" },
 
1
  import { env } from "$env/dynamic/private";
2
  import { generateFromDefaultEndpoint } from "$lib/server/generateFromDefaultEndpoint";
3
+ import type { Message } from "$lib/types/Message";
4
  import { logger } from "$lib/server/logger";
5
 
6
  export async function summarize(prompt: string) {
 
8
  return prompt.split(/\s+/g).slice(0, 5).join(" ");
9
  }
10
 
11
+ const messages: Array<Omit<Message, "id">> = [
12
  { from: "user", content: "Who is the president of Gabon?" },
13
  { from: "assistant", content: "🇬🇦 President of Gabon" },
14
  { from: "user", content: "Who is Julien Chaumond?" },
src/lib/server/websearch/search/generateQuery.ts CHANGED
@@ -1,6 +1,5 @@
1
  import type { Message } from "$lib/types/Message";
2
  import { format } from "date-fns";
3
- import type { EndpointMessage } from "../../endpoints/endpoints";
4
  import { generateFromDefaultEndpoint } from "../../generateFromDefaultEndpoint";
5
 
6
  export async function generateQuery(messages: Message[]) {
@@ -10,7 +9,7 @@ export async function generateQuery(messages: Message[]) {
10
 
11
  const lastMessage = userMessages.slice(-1)[0];
12
 
13
- const convQuery: Array<EndpointMessage> = [
14
  {
15
  from: "user",
16
  content: `Previous Questions:
 
1
  import type { Message } from "$lib/types/Message";
2
  import { format } from "date-fns";
 
3
  import { generateFromDefaultEndpoint } from "../../generateFromDefaultEndpoint";
4
 
5
  export async function generateQuery(messages: Message[]) {
 
9
 
10
  const lastMessage = userMessages.slice(-1)[0];
11
 
12
+ const convQuery: Array<Omit<Message, "id">> = [
13
  {
14
  from: "user",
15
  content: `Previous Questions:
src/lib/types/Message.ts CHANGED
@@ -11,11 +11,7 @@ export type Message = Partial<Timestamps> & {
11
  webSearchId?: WebSearch["_id"]; // legacy version
12
  webSearch?: WebSearch;
13
  score?: -1 | 0 | 1;
14
- /**
15
- * Either contains the base64 encoded image data
16
- * or the hash of the file stored on the server
17
- **/
18
- files?: MessageFile[];
19
  interrupted?: boolean;
20
 
21
  // needed for conversation trees
@@ -24,9 +20,3 @@ export type Message = Partial<Timestamps> & {
24
  // goes one level deep
25
  children?: Message["id"][];
26
  };
27
-
28
- export type MessageFile = {
29
- type: "hash" | "base64";
30
- value: string;
31
- mime: string;
32
- };
 
11
  webSearchId?: WebSearch["_id"]; // legacy version
12
  webSearch?: WebSearch;
13
  score?: -1 | 0 | 1;
14
+ files?: string[]; // can contain either the hash of the file or the b64 encoded image data on the client side when uploading
 
 
 
 
15
  interrupted?: boolean;
16
 
17
  // needed for conversation trees
 
20
  // goes one level deep
21
  children?: Message["id"][];
22
  };
 
 
 
 
 
 
src/lib/utils/messageUpdates.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { MessageFile } from "$lib/types/Message";
2
  import type { MessageUpdate, TextStreamUpdate } from "$lib/types/MessageUpdate";
3
 
4
  type MessageUpdateRequestOptions = {
@@ -8,7 +7,7 @@ type MessageUpdateRequestOptions = {
8
  isRetry: boolean;
9
  isContinue: boolean;
10
  webSearch: boolean;
11
- files?: MessageFile[];
12
  };
13
  export async function fetchMessageUpdates(
14
  conversationId: string,
 
 
1
  import type { MessageUpdate, TextStreamUpdate } from "$lib/types/MessageUpdate";
2
 
3
  type MessageUpdateRequestOptions = {
 
7
  isRetry: boolean;
8
  isContinue: boolean;
9
  webSearch: boolean;
10
+ files?: string[];
11
  };
12
  export async function fetchMessageUpdates(
13
  conversationId: string,
src/routes/conversation/[id]/+page.svelte CHANGED
@@ -75,10 +75,20 @@
75
  loading = true;
76
  pending = true;
77
 
78
- const base64Files = await Promise.all(
79
- (files ?? []).map((file) =>
80
- file2base64(file).then((value) => ({ type: "base64" as const, value, mime: file.type }))
81
- )
 
 
 
 
 
 
 
 
 
 
82
  );
83
 
84
  let messageToWriteToId: Message["id"] | undefined = undefined;
@@ -110,11 +120,7 @@
110
  messages,
111
  rootMessageId: data.rootMessageId,
112
  },
113
- {
114
- from: "user",
115
- content: prompt,
116
- files: messageToRetry.files,
117
- },
118
  messageId
119
  );
120
  messageToWriteToId = addChildren(
@@ -122,7 +128,7 @@
122
  messages,
123
  rootMessageId: data.rootMessageId,
124
  },
125
- { from: "assistant", content: "" },
126
  newUserMessageId
127
  );
128
  } else if (messageToRetry?.from === "assistant") {
@@ -148,7 +154,7 @@
148
  {
149
  from: "user",
150
  content: prompt ?? "",
151
- files: base64Files,
152
  createdAt: new Date(),
153
  updatedAt: new Date(),
154
  },
@@ -175,7 +181,6 @@
175
  }
176
 
177
  messages = [...messages];
178
- const userMessage = messages.find((message) => message.id === messageId);
179
  const messageToWriteTo = messages.find((message) => message.id === messageToWriteToId);
180
  if (!messageToWriteTo) {
181
  throw new Error("Message to write to not found");
@@ -193,7 +198,7 @@
193
  isRetry,
194
  isContinue,
195
  webSearch: !hasAssistant && $webSearchParameters.useSearch,
196
- files: isRetry ? userMessage?.files : base64Files,
197
  },
198
  messageUpdatesAbortController.signal
199
  ).catch((err) => {
 
75
  loading = true;
76
  pending = true;
77
 
78
+ const module = await import("browser-image-resizer");
79
+ // currently, only IDEFICS is supported by TGI
80
+ // the size of images is hardcoded to 224x224 in TGI
81
+ // this will need to be configurable when support for more models is added
82
+ const resizedImages = await Promise.all(
83
+ files.map(async (file) => {
84
+ return await module
85
+ .readAndCompressImage(file, {
86
+ maxHeight: 224,
87
+ maxWidth: 224,
88
+ quality: 1,
89
+ })
90
+ .then(async (el) => await file2base64(el as File));
91
+ })
92
  );
93
 
94
  let messageToWriteToId: Message["id"] | undefined = undefined;
 
120
  messages,
121
  rootMessageId: data.rootMessageId,
122
  },
123
+ { from: "user", content: prompt },
 
 
 
 
124
  messageId
125
  );
126
  messageToWriteToId = addChildren(
 
128
  messages,
129
  rootMessageId: data.rootMessageId,
130
  },
131
+ { from: "assistant", content: "", files: resizedImages },
132
  newUserMessageId
133
  );
134
  } else if (messageToRetry?.from === "assistant") {
 
154
  {
155
  from: "user",
156
  content: prompt ?? "",
157
+ files: resizedImages,
158
  createdAt: new Date(),
159
  updatedAt: new Date(),
160
  },
 
181
  }
182
 
183
  messages = [...messages];
 
184
  const messageToWriteTo = messages.find((message) => message.id === messageToWriteToId);
185
  if (!messageToWriteTo) {
186
  throw new Error("Message to write to not found");
 
198
  isRetry,
199
  isContinue,
200
  webSearch: !hasAssistant && $webSearchParameters.useSearch,
201
+ files: isRetry ? undefined : resizedImages,
202
  },
203
  messageUpdatesAbortController.signal
204
  ).catch((err) => {
src/routes/conversation/[id]/+server.ts CHANGED
@@ -13,13 +13,14 @@ import { runWebSearch } from "$lib/server/websearch/runWebSearch";
13
  import { AbortedGenerations } from "$lib/server/abortedGenerations";
14
  import { summarize } from "$lib/server/summarize";
15
  import { uploadFile } from "$lib/server/files/uploadFile";
 
16
  import type { Assistant } from "$lib/types/Assistant";
17
  import { convertLegacyConversation } from "$lib/utils/tree/convertLegacyConversation";
18
  import { isMessageId } from "$lib/utils/tree/isMessageId";
19
  import { buildSubtree } from "$lib/utils/tree/buildSubtree.js";
20
  import { addChildren } from "$lib/utils/tree/addChildren.js";
21
  import { addSibling } from "$lib/utils/tree/addSibling.js";
22
- import { preprocessMessages } from "$lib/server/endpoints/preprocessMessages.js";
23
  import { usageLimits } from "$lib/server/usageLimits";
24
  import { isURLLocal } from "$lib/server/isURLLocal.js";
25
  import { logger } from "$lib/server/logger.js";
@@ -133,7 +134,7 @@ export async function POST({ request, locals, params, getClientAddress }) {
133
  is_retry: isRetry,
134
  is_continue: isContinue,
135
  web_search: webSearch,
136
- files: inputFiles,
137
  } = z
138
  .object({
139
  id: z.string().uuid().refine(isMessageId).optional(), // parent message id to append to for a normal message, or the message id for a retry/continue
@@ -146,43 +147,44 @@ export async function POST({ request, locals, params, getClientAddress }) {
146
  is_retry: z.optional(z.boolean()),
147
  is_continue: z.optional(z.boolean()),
148
  web_search: z.optional(z.boolean()),
149
- files: z.optional(
150
- z.array(
151
- z.object({
152
- type: z.literal("base64").or(z.literal("hash")),
153
- value: z.string(),
154
- mime: z.string(),
155
- })
156
- )
157
- ),
158
  })
159
  .parse(json);
160
 
161
  if (usageLimits?.messageLength && (newPrompt?.length ?? 0) > usageLimits.messageLength) {
162
  throw error(400, "Message too long.");
163
  }
 
 
164
 
165
- // each file is either:
166
- // base64 string requiring upload to the server
167
- // hash pointing to an existing file
168
- const hashFiles = inputFiles?.filter((file) => file.type === "hash") ?? [];
169
- const b64Files =
170
- inputFiles
171
- ?.filter((file) => file.type !== "hash")
172
- .map((file) => {
173
- const blob = Buffer.from(file.value, "base64");
174
- return new File([blob], "file", { type: file.mime });
175
- }) ?? [];
176
 
177
  // check sizes
178
- // todo: make configurable
179
- if (b64Files.some((file) => file.size > 10 * 1024 * 1024)) {
180
- throw error(413, "File too large, should be <10MB");
 
 
 
 
 
 
 
 
 
 
 
 
181
  }
182
 
183
- const uploadedFiles = await Promise.all(b64Files.map((file) => uploadFile(file, conv))).then(
184
- (files) => [...files, ...hashFiles]
185
- );
 
 
186
 
187
  // we will append tokens to the content of this message
188
  let messageToWriteToId: Message["id"] | undefined = undefined;
@@ -214,13 +216,7 @@ export async function POST({ request, locals, params, getClientAddress }) {
214
  // add a children to that sibling, where we can write to
215
  const newUserMessageId = addSibling(
216
  conv,
217
- {
218
- from: "user",
219
- content: newPrompt,
220
- files: uploadedFiles,
221
- createdAt: new Date(),
222
- updatedAt: new Date(),
223
- },
224
  messageId
225
  );
226
  messageToWriteToId = addChildren(
@@ -228,6 +224,7 @@ export async function POST({ request, locals, params, getClientAddress }) {
228
  {
229
  from: "assistant",
230
  content: "",
 
231
  createdAt: new Date(),
232
  updatedAt: new Date(),
233
  },
@@ -253,7 +250,7 @@ export async function POST({ request, locals, params, getClientAddress }) {
253
  {
254
  from: "user",
255
  content: newPrompt ?? "",
256
- files: uploadedFiles,
257
  createdAt: new Date(),
258
  updatedAt: new Date(),
259
  },
@@ -414,9 +411,10 @@ export async function POST({ request, locals, params, getClientAddress }) {
414
  }
415
 
416
  // inject websearch result & optionally images into the messages
417
- const processedMessages = preprocessMessages(
418
  messagesForPrompt,
419
  messageToWriteTo.webSearch,
 
420
  convId
421
  );
422
 
@@ -431,7 +429,7 @@ export async function POST({ request, locals, params, getClientAddress }) {
431
  try {
432
  const endpoint = await model.getEndpoint();
433
  for await (const output of await endpoint({
434
- messages: await processedMessages,
435
  preprompt,
436
  continueMessage: isContinue,
437
  generateSettings: assistant?.generateSettings,
 
13
  import { AbortedGenerations } from "$lib/server/abortedGenerations";
14
  import { summarize } from "$lib/server/summarize";
15
  import { uploadFile } from "$lib/server/files/uploadFile";
16
+ import sizeof from "image-size";
17
  import type { Assistant } from "$lib/types/Assistant";
18
  import { convertLegacyConversation } from "$lib/utils/tree/convertLegacyConversation";
19
  import { isMessageId } from "$lib/utils/tree/isMessageId";
20
  import { buildSubtree } from "$lib/utils/tree/buildSubtree.js";
21
  import { addChildren } from "$lib/utils/tree/addChildren.js";
22
  import { addSibling } from "$lib/utils/tree/addSibling.js";
23
+ import { preprocessMessages } from "$lib/server/preprocessMessages.js";
24
  import { usageLimits } from "$lib/server/usageLimits";
25
  import { isURLLocal } from "$lib/server/isURLLocal.js";
26
  import { logger } from "$lib/server/logger.js";
 
134
  is_retry: isRetry,
135
  is_continue: isContinue,
136
  web_search: webSearch,
137
+ files: b64files,
138
  } = z
139
  .object({
140
  id: z.string().uuid().refine(isMessageId).optional(), // parent message id to append to for a normal message, or the message id for a retry/continue
 
147
  is_retry: z.optional(z.boolean()),
148
  is_continue: z.optional(z.boolean()),
149
  web_search: z.optional(z.boolean()),
150
+ files: z.optional(z.array(z.string())),
 
 
 
 
 
 
 
 
151
  })
152
  .parse(json);
153
 
154
  if (usageLimits?.messageLength && (newPrompt?.length ?? 0) > usageLimits.messageLength) {
155
  throw error(400, "Message too long.");
156
  }
157
+ // files is an array of base64 strings encoding Blob objects
158
+ // we need to convert this array to an array of File objects
159
 
160
+ const files = b64files?.map((file) => {
161
+ const blob = Buffer.from(file, "base64");
162
+ return new File([blob], "image.png");
163
+ });
 
 
 
 
 
 
 
164
 
165
  // check sizes
166
+ if (files) {
167
+ const filechecks = await Promise.all(
168
+ files.map(async (file) => {
169
+ const dimensions = sizeof(Buffer.from(await file.arrayBuffer()));
170
+ return (
171
+ file.size > 2 * 1024 * 1024 ||
172
+ (dimensions.width ?? 0) > 224 ||
173
+ (dimensions.height ?? 0) > 224
174
+ );
175
+ })
176
+ );
177
+
178
+ if (filechecks.some((check) => check)) {
179
+ throw error(413, "File too large, should be <2MB and 224x224 max.");
180
+ }
181
  }
182
 
183
+ let hashes: undefined | string[];
184
+
185
+ if (files) {
186
+ hashes = await Promise.all(files.map(async (file) => await uploadFile(file, conv)));
187
+ }
188
 
189
  // we will append tokens to the content of this message
190
  let messageToWriteToId: Message["id"] | undefined = undefined;
 
216
  // add a children to that sibling, where we can write to
217
  const newUserMessageId = addSibling(
218
  conv,
219
+ { from: "user", content: newPrompt, createdAt: new Date(), updatedAt: new Date() },
 
 
 
 
 
 
220
  messageId
221
  );
222
  messageToWriteToId = addChildren(
 
224
  {
225
  from: "assistant",
226
  content: "",
227
+ files: hashes,
228
  createdAt: new Date(),
229
  updatedAt: new Date(),
230
  },
 
250
  {
251
  from: "user",
252
  content: newPrompt ?? "",
253
+ files: hashes,
254
  createdAt: new Date(),
255
  updatedAt: new Date(),
256
  },
 
411
  }
412
 
413
  // inject websearch result & optionally images into the messages
414
+ const processedMessages = await preprocessMessages(
415
  messagesForPrompt,
416
  messageToWriteTo.webSearch,
417
+ model.multimodal,
418
  convId
419
  );
420
 
 
429
  try {
430
  const endpoint = await model.getEndpoint();
431
  for await (const output of await endpoint({
432
+ messages: processedMessages,
433
  preprompt,
434
  continueMessage: isContinue,
435
  generateSettings: assistant?.generateSettings,
src/routes/conversation/[id]/output/[sha256]/+server.ts CHANGED
@@ -39,9 +39,9 @@ export const GET: RequestHandler = async ({ locals, params }) => {
39
  }
40
  }
41
 
42
- const { value, mime } = await downloadFile(sha256, params.id);
43
 
44
- return new Response(Buffer.from(value, "base64"), {
45
  headers: {
46
  "Content-Type": mime ?? "application/octet-stream",
47
  },
 
39
  }
40
  }
41
 
42
+ const { content, mime } = await downloadFile(sha256, params.id);
43
 
44
+ return new Response(content, {
45
  headers: {
46
  "Content-Type": mime ?? "application/octet-stream",
47
  },