Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Generic Multimodal Support Fixed (#1147)
Browse files* feat: multimodal anthropic support
* docs: add claude haiku and multimodal support
* feat: uploaded file detection and image conversion
* fix deps with sharp
* fix resvg deps?
* fix: image conversion, retry with files
* feat: generic image processing and size target
* docs: multimodal review comments
Co-authored-by: Mishig <[email protected]>
* docs: multimodal review comments
Co-authored-by: Mishig <[email protected]>
* feat: review comment resolution
* fix: type error on image params
* feat: add multimodal for vertex ai anthropic
* style: uploadFile timeout number
Co-authored-by: Mishig <[email protected]>
* fix: TGI endpoints expecting multimodal
---------
Co-authored-by: Nathan Sarrazin <[email protected]>
Co-authored-by: Mishig <[email protected]>
- README.md +25 -2
- package-lock.json +238 -161
- package.json +4 -3
- src/lib/components/chat/ChatMessage.svelte +4 -4
- src/lib/components/chat/ChatWindow.svelte +5 -3
- src/lib/server/endpoints/anthropic/endpointAnthropic.ts +17 -13
- src/lib/server/endpoints/anthropic/endpointAnthropicVertex.ts +16 -12
- src/lib/server/endpoints/anthropic/utils.ts +44 -0
- src/lib/server/endpoints/endpoints.ts +4 -1
- src/lib/server/endpoints/images.ts +211 -0
- src/lib/server/endpoints/openai/endpointOai.ts +74 -6
- src/lib/server/endpoints/preprocessMessages.ts +56 -0
- src/lib/server/endpoints/tgi/endpointTgi.ts +50 -5
- src/lib/server/files/downloadFile.ts +4 -3
- src/lib/server/files/uploadFile.ts +11 -5
- src/lib/server/generateFromDefaultEndpoint.ts +2 -2
- src/lib/server/models.ts +1 -1
- src/lib/server/preprocessMessages.ts +0 -61
- src/lib/server/summarize.ts +2 -2
- src/lib/server/websearch/search/generateQuery.ts +2 -1
- src/lib/types/Message.ts +11 -1
- src/lib/utils/messageUpdates.ts +2 -1
- src/routes/conversation/[id]/+page.svelte +13 -18
- src/routes/conversation/[id]/+server.ts +39 -36
- src/routes/conversation/[id]/output/[sha256]/+server.ts +2 -2
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
|
234 |
|
235 |
```env
|
236 |
{
|
@@ -465,14 +465,34 @@ MODELS=`[
|
|
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,6 +511,7 @@ MODELS=`[
|
|
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,6 +537,7 @@ MODELS=`[
|
|
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,6 +556,7 @@ MODELS=`[
|
|
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 |
},
|
|
|
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 |
|
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 |
"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 |
"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 |
"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 |
},
|
package-lock.json
CHANGED
@@ -13,13 +13,14 @@
|
|
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.
|
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,7 +42,7 @@
|
|
41 |
"satori-html": "^0.3.2",
|
42 |
"sbd": "^1.0.19",
|
43 |
"serpapi": "^1.1.1",
|
44 |
-
"sharp": "^0.33.
|
45 |
"tailwind-scrollbar": "^3.0.0",
|
46 |
"tailwindcss": "^3.4.0",
|
47 |
"uuid": "^9.0.1",
|
@@ -88,7 +89,7 @@
|
|
88 |
"@google-cloud/vertexai": "^1.1.0",
|
89 |
"aws4fetch": "^1.0.17",
|
90 |
"cohere-ai": "^7.9.0",
|
91 |
-
"openai": "^4.
|
92 |
}
|
93 |
},
|
94 |
"node_modules/@alloc/quick-lru": {
|
@@ -237,9 +238,9 @@
|
|
237 |
}
|
238 |
},
|
239 |
"node_modules/@emnapi/runtime": {
|
240 |
-
"version": "
|
241 |
-
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-
|
242 |
-
"integrity": "sha512-
|
243 |
"optional": true,
|
244 |
"dependencies": {
|
245 |
"tslib": "^2.4.0"
|
@@ -811,9 +812,9 @@
|
|
811 |
}
|
812 |
},
|
813 |
"node_modules/@img/sharp-darwin-arm64": {
|
814 |
-
"version": "0.33.
|
815 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.
|
816 |
-
"integrity": "sha512-
|
817 |
"cpu": [
|
818 |
"arm64"
|
819 |
],
|
@@ -832,13 +833,13 @@
|
|
832 |
"url": "https://opencollective.com/libvips"
|
833 |
},
|
834 |
"optionalDependencies": {
|
835 |
-
"@img/sharp-libvips-darwin-arm64": "1.0.
|
836 |
}
|
837 |
},
|
838 |
"node_modules/@img/sharp-darwin-x64": {
|
839 |
-
"version": "0.33.
|
840 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.
|
841 |
-
"integrity": "sha512
|
842 |
"cpu": [
|
843 |
"x64"
|
844 |
],
|
@@ -857,13 +858,13 @@
|
|
857 |
"url": "https://opencollective.com/libvips"
|
858 |
},
|
859 |
"optionalDependencies": {
|
860 |
-
"@img/sharp-libvips-darwin-x64": "1.0.
|
861 |
}
|
862 |
},
|
863 |
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
864 |
-
"version": "1.0.
|
865 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.
|
866 |
-
"integrity": "sha512-
|
867 |
"cpu": [
|
868 |
"arm64"
|
869 |
],
|
@@ -882,9 +883,9 @@
|
|
882 |
}
|
883 |
},
|
884 |
"node_modules/@img/sharp-libvips-darwin-x64": {
|
885 |
-
"version": "1.0.
|
886 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.
|
887 |
-
"integrity": "sha512-
|
888 |
"cpu": [
|
889 |
"x64"
|
890 |
],
|
@@ -903,9 +904,9 @@
|
|
903 |
}
|
904 |
},
|
905 |
"node_modules/@img/sharp-libvips-linux-arm": {
|
906 |
-
"version": "1.0.
|
907 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.
|
908 |
-
"integrity": "sha512-
|
909 |
"cpu": [
|
910 |
"arm"
|
911 |
],
|
@@ -924,9 +925,9 @@
|
|
924 |
}
|
925 |
},
|
926 |
"node_modules/@img/sharp-libvips-linux-arm64": {
|
927 |
-
"version": "1.0.
|
928 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.
|
929 |
-
"integrity": "sha512-
|
930 |
"cpu": [
|
931 |
"arm64"
|
932 |
],
|
@@ -945,9 +946,9 @@
|
|
945 |
}
|
946 |
},
|
947 |
"node_modules/@img/sharp-libvips-linux-s390x": {
|
948 |
-
"version": "1.0.
|
949 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.
|
950 |
-
"integrity": "sha512-
|
951 |
"cpu": [
|
952 |
"s390x"
|
953 |
],
|
@@ -966,9 +967,9 @@
|
|
966 |
}
|
967 |
},
|
968 |
"node_modules/@img/sharp-libvips-linux-x64": {
|
969 |
-
"version": "1.0.
|
970 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.
|
971 |
-
"integrity": "sha512-
|
972 |
"cpu": [
|
973 |
"x64"
|
974 |
],
|
@@ -987,9 +988,9 @@
|
|
987 |
}
|
988 |
},
|
989 |
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
990 |
-
"version": "1.0.
|
991 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.
|
992 |
-
"integrity": "sha512-
|
993 |
"cpu": [
|
994 |
"arm64"
|
995 |
],
|
@@ -1008,9 +1009,9 @@
|
|
1008 |
}
|
1009 |
},
|
1010 |
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
1011 |
-
"version": "1.0.
|
1012 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.
|
1013 |
-
"integrity": "sha512-
|
1014 |
"cpu": [
|
1015 |
"x64"
|
1016 |
],
|
@@ -1029,9 +1030,9 @@
|
|
1029 |
}
|
1030 |
},
|
1031 |
"node_modules/@img/sharp-linux-arm": {
|
1032 |
-
"version": "0.33.
|
1033 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.
|
1034 |
-
"integrity": "sha512-
|
1035 |
"cpu": [
|
1036 |
"arm"
|
1037 |
],
|
@@ -1050,13 +1051,13 @@
|
|
1050 |
"url": "https://opencollective.com/libvips"
|
1051 |
},
|
1052 |
"optionalDependencies": {
|
1053 |
-
"@img/sharp-libvips-linux-arm": "1.0.
|
1054 |
}
|
1055 |
},
|
1056 |
"node_modules/@img/sharp-linux-arm64": {
|
1057 |
-
"version": "0.33.
|
1058 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.
|
1059 |
-
"integrity": "sha512-
|
1060 |
"cpu": [
|
1061 |
"arm64"
|
1062 |
],
|
@@ -1075,13 +1076,13 @@
|
|
1075 |
"url": "https://opencollective.com/libvips"
|
1076 |
},
|
1077 |
"optionalDependencies": {
|
1078 |
-
"@img/sharp-libvips-linux-arm64": "1.0.
|
1079 |
}
|
1080 |
},
|
1081 |
"node_modules/@img/sharp-linux-s390x": {
|
1082 |
-
"version": "0.33.
|
1083 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.
|
1084 |
-
"integrity": "sha512-
|
1085 |
"cpu": [
|
1086 |
"s390x"
|
1087 |
],
|
@@ -1100,13 +1101,13 @@
|
|
1100 |
"url": "https://opencollective.com/libvips"
|
1101 |
},
|
1102 |
"optionalDependencies": {
|
1103 |
-
"@img/sharp-libvips-linux-s390x": "1.0.
|
1104 |
}
|
1105 |
},
|
1106 |
"node_modules/@img/sharp-linux-x64": {
|
1107 |
-
"version": "0.33.
|
1108 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.
|
1109 |
-
"integrity": "sha512-
|
1110 |
"cpu": [
|
1111 |
"x64"
|
1112 |
],
|
@@ -1125,13 +1126,13 @@
|
|
1125 |
"url": "https://opencollective.com/libvips"
|
1126 |
},
|
1127 |
"optionalDependencies": {
|
1128 |
-
"@img/sharp-libvips-linux-x64": "1.0.
|
1129 |
}
|
1130 |
},
|
1131 |
"node_modules/@img/sharp-linuxmusl-arm64": {
|
1132 |
-
"version": "0.33.
|
1133 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.
|
1134 |
-
"integrity": "sha512-
|
1135 |
"cpu": [
|
1136 |
"arm64"
|
1137 |
],
|
@@ -1150,13 +1151,13 @@
|
|
1150 |
"url": "https://opencollective.com/libvips"
|
1151 |
},
|
1152 |
"optionalDependencies": {
|
1153 |
-
"@img/sharp-libvips-linuxmusl-arm64": "1.0.
|
1154 |
}
|
1155 |
},
|
1156 |
"node_modules/@img/sharp-linuxmusl-x64": {
|
1157 |
-
"version": "0.33.
|
1158 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.
|
1159 |
-
"integrity": "sha512
|
1160 |
"cpu": [
|
1161 |
"x64"
|
1162 |
],
|
@@ -1175,19 +1176,19 @@
|
|
1175 |
"url": "https://opencollective.com/libvips"
|
1176 |
},
|
1177 |
"optionalDependencies": {
|
1178 |
-
"@img/sharp-libvips-linuxmusl-x64": "1.0.
|
1179 |
}
|
1180 |
},
|
1181 |
"node_modules/@img/sharp-wasm32": {
|
1182 |
-
"version": "0.33.
|
1183 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.
|
1184 |
-
"integrity": "sha512-
|
1185 |
"cpu": [
|
1186 |
"wasm32"
|
1187 |
],
|
1188 |
"optional": true,
|
1189 |
"dependencies": {
|
1190 |
-
"@emnapi/runtime": "^
|
1191 |
},
|
1192 |
"engines": {
|
1193 |
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
@@ -1200,9 +1201,9 @@
|
|
1200 |
}
|
1201 |
},
|
1202 |
"node_modules/@img/sharp-win32-ia32": {
|
1203 |
-
"version": "0.33.
|
1204 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.
|
1205 |
-
"integrity": "sha512-
|
1206 |
"cpu": [
|
1207 |
"ia32"
|
1208 |
],
|
@@ -1221,9 +1222,9 @@
|
|
1221 |
}
|
1222 |
},
|
1223 |
"node_modules/@img/sharp-win32-x64": {
|
1224 |
-
"version": "0.33.
|
1225 |
-
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.
|
1226 |
-
"integrity": "sha512-
|
1227 |
"cpu": [
|
1228 |
"x64"
|
1229 |
],
|
@@ -1444,31 +1445,31 @@
|
|
1444 |
"integrity": "sha512-yvwa+aCyYI/UjeD39BnpMypG8N06l86wIDW1/PAc6ihBRnodIfZDwccxQN3n1t74wduzaz74m4ZMHZnB06567Q=="
|
1445 |
},
|
1446 |
"node_modules/@resvg/resvg-js": {
|
1447 |
-
"version": "2.6.
|
1448 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.
|
1449 |
-
"integrity": "sha512-
|
1450 |
"engines": {
|
1451 |
"node": ">= 10"
|
1452 |
},
|
1453 |
"optionalDependencies": {
|
1454 |
-
"@resvg/resvg-js-android-arm-eabi": "2.6.
|
1455 |
-
"@resvg/resvg-js-android-arm64": "2.6.
|
1456 |
-
"@resvg/resvg-js-darwin-arm64": "2.6.
|
1457 |
-
"@resvg/resvg-js-darwin-x64": "2.6.
|
1458 |
-
"@resvg/resvg-js-linux-arm-gnueabihf": "2.6.
|
1459 |
-
"@resvg/resvg-js-linux-arm64-gnu": "2.6.
|
1460 |
-
"@resvg/resvg-js-linux-arm64-musl": "2.6.
|
1461 |
-
"@resvg/resvg-js-linux-x64-gnu": "2.6.
|
1462 |
-
"@resvg/resvg-js-linux-x64-musl": "2.6.
|
1463 |
-
"@resvg/resvg-js-win32-arm64-msvc": "2.6.
|
1464 |
-
"@resvg/resvg-js-win32-ia32-msvc": "2.6.
|
1465 |
-
"@resvg/resvg-js-win32-x64-msvc": "2.6.
|
1466 |
}
|
1467 |
},
|
1468 |
"node_modules/@resvg/resvg-js-android-arm-eabi": {
|
1469 |
-
"version": "2.6.
|
1470 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.
|
1471 |
-
"integrity": "sha512-
|
1472 |
"cpu": [
|
1473 |
"arm"
|
1474 |
],
|
@@ -1481,9 +1482,9 @@
|
|
1481 |
}
|
1482 |
},
|
1483 |
"node_modules/@resvg/resvg-js-android-arm64": {
|
1484 |
-
"version": "2.6.
|
1485 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.
|
1486 |
-
"integrity": "sha512-
|
1487 |
"cpu": [
|
1488 |
"arm64"
|
1489 |
],
|
@@ -1496,9 +1497,9 @@
|
|
1496 |
}
|
1497 |
},
|
1498 |
"node_modules/@resvg/resvg-js-darwin-arm64": {
|
1499 |
-
"version": "2.6.
|
1500 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.
|
1501 |
-
"integrity": "sha512-
|
1502 |
"cpu": [
|
1503 |
"arm64"
|
1504 |
],
|
@@ -1511,9 +1512,9 @@
|
|
1511 |
}
|
1512 |
},
|
1513 |
"node_modules/@resvg/resvg-js-darwin-x64": {
|
1514 |
-
"version": "2.6.
|
1515 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.
|
1516 |
-
"integrity": "sha512-
|
1517 |
"cpu": [
|
1518 |
"x64"
|
1519 |
],
|
@@ -1526,9 +1527,9 @@
|
|
1526 |
}
|
1527 |
},
|
1528 |
"node_modules/@resvg/resvg-js-linux-arm-gnueabihf": {
|
1529 |
-
"version": "2.6.
|
1530 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.
|
1531 |
-
"integrity": "sha512-
|
1532 |
"cpu": [
|
1533 |
"arm"
|
1534 |
],
|
@@ -1541,9 +1542,9 @@
|
|
1541 |
}
|
1542 |
},
|
1543 |
"node_modules/@resvg/resvg-js-linux-arm64-gnu": {
|
1544 |
-
"version": "2.6.
|
1545 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.
|
1546 |
-
"integrity": "sha512-
|
1547 |
"cpu": [
|
1548 |
"arm64"
|
1549 |
],
|
@@ -1556,9 +1557,9 @@
|
|
1556 |
}
|
1557 |
},
|
1558 |
"node_modules/@resvg/resvg-js-linux-arm64-musl": {
|
1559 |
-
"version": "2.6.
|
1560 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.
|
1561 |
-
"integrity": "sha512-
|
1562 |
"cpu": [
|
1563 |
"arm64"
|
1564 |
],
|
@@ -1571,9 +1572,9 @@
|
|
1571 |
}
|
1572 |
},
|
1573 |
"node_modules/@resvg/resvg-js-linux-x64-gnu": {
|
1574 |
-
"version": "2.6.
|
1575 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.
|
1576 |
-
"integrity": "sha512-
|
1577 |
"cpu": [
|
1578 |
"x64"
|
1579 |
],
|
@@ -1586,9 +1587,9 @@
|
|
1586 |
}
|
1587 |
},
|
1588 |
"node_modules/@resvg/resvg-js-linux-x64-musl": {
|
1589 |
-
"version": "2.6.
|
1590 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.
|
1591 |
-
"integrity": "sha512-
|
1592 |
"cpu": [
|
1593 |
"x64"
|
1594 |
],
|
@@ -1601,9 +1602,9 @@
|
|
1601 |
}
|
1602 |
},
|
1603 |
"node_modules/@resvg/resvg-js-win32-arm64-msvc": {
|
1604 |
-
"version": "2.6.
|
1605 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.
|
1606 |
-
"integrity": "sha512-
|
1607 |
"cpu": [
|
1608 |
"arm64"
|
1609 |
],
|
@@ -1616,9 +1617,9 @@
|
|
1616 |
}
|
1617 |
},
|
1618 |
"node_modules/@resvg/resvg-js-win32-ia32-msvc": {
|
1619 |
-
"version": "2.6.
|
1620 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.
|
1621 |
-
"integrity": "sha512-
|
1622 |
"cpu": [
|
1623 |
"ia32"
|
1624 |
],
|
@@ -1631,9 +1632,9 @@
|
|
1631 |
}
|
1632 |
},
|
1633 |
"node_modules/@resvg/resvg-js-win32-x64-msvc": {
|
1634 |
-
"version": "2.6.
|
1635 |
-
"resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.
|
1636 |
-
"integrity": "sha512-
|
1637 |
"cpu": [
|
1638 |
"x64"
|
1639 |
],
|
@@ -2075,6 +2076,11 @@
|
|
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,9 +3837,9 @@
|
|
3831 |
}
|
3832 |
},
|
3833 |
"node_modules/detect-libc": {
|
3834 |
-
"version": "2.0.
|
3835 |
-
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.
|
3836 |
-
"integrity": "sha512-
|
3837 |
"engines": {
|
3838 |
"node": ">=8"
|
3839 |
}
|
@@ -4556,6 +4562,22 @@
|
|
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,16 +6407,15 @@
|
|
6385 |
}
|
6386 |
},
|
6387 |
"node_modules/openai": {
|
6388 |
-
"version": "4.
|
6389 |
-
"resolved": "https://registry.npmjs.org/openai/-/openai-4.
|
6390 |
-
"integrity": "sha512-
|
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,6 +6641,18 @@
|
|
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,6 +7508,21 @@
|
|
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,12 +7784,9 @@
|
|
7736 |
"integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="
|
7737 |
},
|
7738 |
"node_modules/semver": {
|
7739 |
-
"version": "7.
|
7740 |
-
"resolved": "https://registry.npmjs.org/semver/-/semver-7.
|
7741 |
-
"integrity": "sha512-
|
7742 |
-
"dependencies": {
|
7743 |
-
"lru-cache": "^6.0.0"
|
7744 |
-
},
|
7745 |
"bin": {
|
7746 |
"semver": "bin/semver.js"
|
7747 |
},
|
@@ -7840,42 +7885,42 @@
|
|
7840 |
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
7841 |
},
|
7842 |
"node_modules/sharp": {
|
7843 |
-
"version": "0.33.
|
7844 |
-
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.
|
7845 |
-
"integrity": "sha512-
|
7846 |
"hasInstallScript": true,
|
7847 |
"dependencies": {
|
7848 |
"color": "^4.2.3",
|
7849 |
-
"detect-libc": "^2.0.
|
7850 |
-
"semver": "^7.
|
7851 |
},
|
7852 |
"engines": {
|
7853 |
-
"libvips": ">=8.15.
|
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.
|
7861 |
-
"@img/sharp-darwin-x64": "0.33.
|
7862 |
-
"@img/sharp-libvips-darwin-arm64": "1.0.
|
7863 |
-
"@img/sharp-libvips-darwin-x64": "1.0.
|
7864 |
-
"@img/sharp-libvips-linux-arm": "1.0.
|
7865 |
-
"@img/sharp-libvips-linux-arm64": "1.0.
|
7866 |
-
"@img/sharp-libvips-linux-s390x": "1.0.
|
7867 |
-
"@img/sharp-libvips-linux-x64": "1.0.
|
7868 |
-
"@img/sharp-libvips-linuxmusl-arm64": "1.0.
|
7869 |
-
"@img/sharp-libvips-linuxmusl-x64": "1.0.
|
7870 |
-
"@img/sharp-linux-arm": "0.33.
|
7871 |
-
"@img/sharp-linux-arm64": "0.33.
|
7872 |
-
"@img/sharp-linux-s390x": "0.33.
|
7873 |
-
"@img/sharp-linux-x64": "0.33.
|
7874 |
-
"@img/sharp-linuxmusl-arm64": "0.33.
|
7875 |
-
"@img/sharp-linuxmusl-x64": "0.33.
|
7876 |
-
"@img/sharp-wasm32": "0.33.
|
7877 |
-
"@img/sharp-win32-ia32": "0.33.
|
7878 |
-
"@img/sharp-win32-x64": "0.33.
|
7879 |
}
|
7880 |
},
|
7881 |
"node_modules/shebang-command": {
|
@@ -8187,6 +8232,22 @@
|
|
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,6 +8770,22 @@
|
|
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",
|
|
|
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 |
"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 |
"@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 |
}
|
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 |
}
|
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 |
"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 |
"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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
"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 |
"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 |
"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 |
"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 |
"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 |
"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 |
}
|
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 |
}
|
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 |
"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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
}
|
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 |
"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 |
}
|
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 |
"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 |
}
|
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 |
"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 |
"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 |
"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 |
"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 |
"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 |
"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",
|
package.json
CHANGED
@@ -57,14 +57,15 @@
|
|
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,7 +87,7 @@
|
|
86 |
"satori-html": "^0.3.2",
|
87 |
"sbd": "^1.0.19",
|
88 |
"serpapi": "^1.1.1",
|
89 |
-
"sharp": "^0.33.
|
90 |
"tailwind-scrollbar": "^3.0.0",
|
91 |
"tailwindcss": "^3.4.0",
|
92 |
"uuid": "^9.0.1",
|
@@ -98,6 +99,6 @@
|
|
98 |
"@google-cloud/vertexai": "^1.1.0",
|
99 |
"aws4fetch": "^1.0.17",
|
100 |
"cohere-ai": "^7.9.0",
|
101 |
-
"openai": "^4.
|
102 |
}
|
103 |
}
|
|
|
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 |
"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 |
"@google-cloud/vertexai": "^1.1.0",
|
100 |
"aws4fetch": "^1.0.17",
|
101 |
"cohere-ai": "^7.9.0",
|
102 |
+
"openai": "^4.44.0"
|
103 |
}
|
104 |
}
|
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.
|
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={
|
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 -->
|
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 |
/>
|
src/lib/components/chat/ChatWindow.svelte
CHANGED
@@ -92,7 +92,9 @@
|
|
92 |
(lastMessage.from === "user" ||
|
93 |
lastMessage.updates?.findIndex((u) => u.type === "status" && u.status === "error") !== -1);
|
94 |
|
95 |
-
$: sources = files
|
|
|
|
|
96 |
|
97 |
function onShare() {
|
98 |
dispatch("share");
|
@@ -229,13 +231,13 @@
|
|
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
|
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
|
239 |
alt="input content"
|
240 |
class="h-full w-full rounded-lg bg-gray-400 object-cover dark:bg-gray-900"
|
241 |
/>
|
|
|
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 |
<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 |
/>
|
src/lib/server/endpoints/anthropic/endpointAnthropic.ts
CHANGED
@@ -1,7 +1,9 @@
|
|
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,12 +13,24 @@ export const endpointAnthropicParametersSchema = z.object({
|
|
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,16 +52,6 @@ export async function endpointAnthropic(
|
|
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,7 +59,7 @@ export async function endpointAnthropic(
|
|
55 |
return (async function* () {
|
56 |
const stream = anthropic.messages.stream({
|
57 |
model: model.id ?? model.name,
|
58 |
-
messages:
|
59 |
max_tokens: parameters?.max_new_tokens,
|
60 |
temperature: parameters?.temperature,
|
61 |
top_p: parameters?.top_p,
|
|
|
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 |
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 |
system = messages[0].content;
|
53 |
}
|
54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
let tokenId = 0;
|
56 |
|
57 |
const parameters = { ...model.parameters, ...generateSettings };
|
|
|
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,
|
src/lib/server/endpoints/anthropic/endpointAnthropicVertex.ts
CHANGED
@@ -1,6 +1,8 @@
|
|
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,12 +12,24 @@ export const endpointAnthropicVertexParametersSchema = z.object({
|
|
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,21 +52,11 @@ export async function endpointAnthropicVertex(
|
|
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:
|
56 |
max_tokens: model.parameters?.max_new_tokens,
|
57 |
temperature: model.parameters?.temperature,
|
58 |
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 |
+
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 |
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 |
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,
|
src/lib/server/endpoints/anthropic/utils.ts
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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,4 +1,5 @@
|
|
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,12 +26,14 @@ import endpointLangserve, {
|
|
25 |
endpointLangserveParametersSchema,
|
26 |
} from "./langserve/endpointLangserve";
|
27 |
|
|
|
28 |
// parameters passed when generating text
|
29 |
export interface EndpointParameters {
|
30 |
-
messages:
|
31 |
preprompt?: Conversation["preprompt"];
|
32 |
continueMessage?: boolean; // used to signal that the last message will be extended
|
33 |
generateSettings?: Partial<Model["parameters"]>;
|
|
|
34 |
}
|
35 |
|
36 |
interface CommonEndpoint {
|
|
|
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 |
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"]>;
|
36 |
+
isMultimodal?: boolean;
|
37 |
}
|
38 |
|
39 |
interface CommonEndpoint {
|
src/lib/server/endpoints/images.ts
ADDED
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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,6 +6,10 @@ 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 |
|
10 |
export const endpointOAIParametersSchema = z.object({
|
11 |
weight: z.number().int().positive().default(1),
|
@@ -19,13 +23,41 @@ export const endpointOAIParametersSchema = z.object({
|
|
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 {
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
let OpenAI;
|
30 |
try {
|
31 |
OpenAI = (await import("openai")).OpenAI;
|
@@ -40,6 +72,8 @@ export async function endpointOai(
|
|
40 |
defaultQuery,
|
41 |
});
|
42 |
|
|
|
|
|
43 |
if (completion === "completions") {
|
44 |
return async ({ messages, preprompt, continueMessage, generateSettings }) => {
|
45 |
const prompt = await buildPrompt({
|
@@ -69,10 +103,8 @@ export async function endpointOai(
|
|
69 |
};
|
70 |
} else if (completion === "chat_completions") {
|
71 |
return async ({ messages, preprompt, generateSettings }) => {
|
72 |
-
let messagesOpenAI
|
73 |
-
|
74 |
-
content: message.content,
|
75 |
-
}));
|
76 |
|
77 |
if (messagesOpenAI?.[0]?.role !== "system") {
|
78 |
messagesOpenAI = [{ role: "system", content: "" }, ...messagesOpenAI];
|
@@ -104,3 +136,39 @@ export async function endpointOai(
|
|
104 |
throw new Error("Invalid completion type");
|
105 |
}
|
106 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
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 |
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 |
};
|
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 |
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 |
+
}
|
src/lib/server/endpoints/preprocessMessages.ts
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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,8 +1,13 @@
|
|
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,14 +16,32 @@ export const endpointTgiParametersSchema = z.object({
|
|
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 } =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
-
return async ({ messages, preprompt, continueMessage, generateSettings }) => {
|
20 |
const prompt = await buildPrompt({
|
21 |
-
messages,
|
22 |
preprompt,
|
23 |
model,
|
24 |
continueMessage,
|
@@ -48,4 +71,26 @@ export function endpointTgi(input: z.input<typeof endpointTgiParametersSchema>):
|
|
48 |
};
|
49 |
}
|
50 |
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
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, isMultimodal }) => {
|
39 |
+
const messagesWithResizedFiles = await Promise.all(
|
40 |
+
messages.map((message) => prepareMessage(Boolean(isMultimodal), message, imageProcessor))
|
41 |
+
);
|
42 |
|
|
|
43 |
const prompt = await buildPrompt({
|
44 |
+
messages: messagesWithResizedFiles,
|
45 |
preprompt,
|
46 |
model,
|
47 |
continueMessage,
|
|
|
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 |
+
isMultimodal: boolean,
|
84 |
+
message: EndpointMessage,
|
85 |
+
imageProcessor: ImageProcessor
|
86 |
+
): Promise<EndpointMessage> {
|
87 |
+
if (!isMultimodal) return message;
|
88 |
+
|
89 |
+
const files = await Promise.all(message.files?.map(imageProcessor) ?? [whiteImage]);
|
90 |
+
const markdowns = files.map(
|
91 |
+
(file) => `})`
|
92 |
+
);
|
93 |
+
const content = message.content + "\n" + markdowns.join("\n ");
|
94 |
+
|
95 |
+
return { ...message, content };
|
96 |
+
}
|
src/lib/server/files/downloadFile.ts
CHANGED
@@ -2,15 +2,16 @@ 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 |
|
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
|
14 |
if (!file) {
|
15 |
throw error(404, "File not found");
|
16 |
}
|
@@ -32,5 +33,5 @@ export async function downloadFile(
|
|
32 |
return fileBuffer;
|
33 |
});
|
34 |
|
35 |
-
return {
|
36 |
}
|
|
|
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 |
return fileBuffer;
|
34 |
});
|
35 |
|
36 |
+
return { type: "base64", value: buffer.toString("base64"), mime };
|
37 |
}
|
src/lib/server/files/uploadFile.ts
CHANGED
@@ -1,21 +1,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:
|
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
|
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
|
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")),
|
20 |
});
|
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 |
}
|
src/lib/server/generateFromDefaultEndpoint.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
import { smallModel } from "$lib/server/models";
|
2 |
-
import type {
|
3 |
|
4 |
export async function generateFromDefaultEndpoint({
|
5 |
messages,
|
6 |
preprompt,
|
7 |
generateSettings,
|
8 |
}: {
|
9 |
-
messages:
|
10 |
preprompt?: string;
|
11 |
generateSettings?: Record<string, unknown>;
|
12 |
}): Promise<string> {
|
|
|
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> {
|
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
DELETED
@@ -1,61 +0,0 @@
|
|
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 `})`;
|
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";
|
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 {
|
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<
|
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 { EndpointMessage } from "./endpoints/endpoints";
|
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<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?" },
|
src/lib/server/websearch/search/generateQuery.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
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,7 +10,7 @@ export async function generateQuery(messages: Message[]) {
|
|
9 |
|
10 |
const lastMessage = userMessages.slice(-1)[0];
|
11 |
|
12 |
-
const convQuery: Array<
|
13 |
{
|
14 |
from: "user",
|
15 |
content: `Previous Questions:
|
|
|
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 |
|
11 |
const lastMessage = userMessages.slice(-1)[0];
|
12 |
|
13 |
+
const convQuery: Array<EndpointMessage> = [
|
14 |
{
|
15 |
from: "user",
|
16 |
content: `Previous Questions:
|
src/lib/types/Message.ts
CHANGED
@@ -11,7 +11,11 @@ export type Message = Partial<Timestamps> & {
|
|
11 |
webSearchId?: WebSearch["_id"]; // legacy version
|
12 |
webSearch?: WebSearch;
|
13 |
score?: -1 | 0 | 1;
|
14 |
-
|
|
|
|
|
|
|
|
|
15 |
interrupted?: boolean;
|
16 |
|
17 |
// needed for conversation trees
|
@@ -20,3 +24,9 @@ export type Message = Partial<Timestamps> & {
|
|
20 |
// goes one level deep
|
21 |
children?: Message["id"][];
|
22 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
// 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 |
+
};
|
src/lib/utils/messageUpdates.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import type { MessageUpdate, TextStreamUpdate } from "$lib/types/MessageUpdate";
|
2 |
|
3 |
type MessageUpdateRequestOptions = {
|
@@ -7,7 +8,7 @@ type MessageUpdateRequestOptions = {
|
|
7 |
isRetry: boolean;
|
8 |
isContinue: boolean;
|
9 |
webSearch: boolean;
|
10 |
-
files?:
|
11 |
};
|
12 |
export async function fetchMessageUpdates(
|
13 |
conversationId: string,
|
|
|
1 |
+
import type { MessageFile } from "$lib/types/Message";
|
2 |
import type { MessageUpdate, TextStreamUpdate } from "$lib/types/MessageUpdate";
|
3 |
|
4 |
type MessageUpdateRequestOptions = {
|
|
|
8 |
isRetry: boolean;
|
9 |
isContinue: boolean;
|
10 |
webSearch: boolean;
|
11 |
+
files?: MessageFile[];
|
12 |
};
|
13 |
export async function fetchMessageUpdates(
|
14 |
conversationId: string,
|
src/routes/conversation/[id]/+page.svelte
CHANGED
@@ -75,20 +75,10 @@
|
|
75 |
loading = true;
|
76 |
pending = true;
|
77 |
|
78 |
-
const
|
79 |
-
|
80 |
-
|
81 |
-
|
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,7 +110,11 @@
|
|
120 |
messages,
|
121 |
rootMessageId: data.rootMessageId,
|
122 |
},
|
123 |
-
{
|
|
|
|
|
|
|
|
|
124 |
messageId
|
125 |
);
|
126 |
messageToWriteToId = addChildren(
|
@@ -128,7 +122,7 @@
|
|
128 |
messages,
|
129 |
rootMessageId: data.rootMessageId,
|
130 |
},
|
131 |
-
{ from: "assistant", content: ""
|
132 |
newUserMessageId
|
133 |
);
|
134 |
} else if (messageToRetry?.from === "assistant") {
|
@@ -154,7 +148,7 @@
|
|
154 |
{
|
155 |
from: "user",
|
156 |
content: prompt ?? "",
|
157 |
-
files:
|
158 |
createdAt: new Date(),
|
159 |
updatedAt: new Date(),
|
160 |
},
|
@@ -181,6 +175,7 @@
|
|
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,7 +193,7 @@
|
|
198 |
isRetry,
|
199 |
isContinue,
|
200 |
webSearch: !hasAssistant && $webSearchParameters.useSearch,
|
201 |
-
files: isRetry ?
|
202 |
},
|
203 |
messageUpdatesAbortController.signal
|
204 |
).catch((err) => {
|
|
|
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 |
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 |
messages,
|
123 |
rootMessageId: data.rootMessageId,
|
124 |
},
|
125 |
+
{ from: "assistant", content: "" },
|
126 |
newUserMessageId
|
127 |
);
|
128 |
} else if (messageToRetry?.from === "assistant") {
|
|
|
148 |
{
|
149 |
from: "user",
|
150 |
content: prompt ?? "",
|
151 |
+
files: base64Files,
|
152 |
createdAt: new Date(),
|
153 |
updatedAt: new Date(),
|
154 |
},
|
|
|
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 |
isRetry,
|
194 |
isContinue,
|
195 |
webSearch: !hasAssistant && $webSearchParameters.useSearch,
|
196 |
+
files: isRetry ? userMessage?.files : base64Files,
|
197 |
},
|
198 |
messageUpdatesAbortController.signal
|
199 |
).catch((err) => {
|
src/routes/conversation/[id]/+server.ts
CHANGED
@@ -13,14 +13,13 @@ 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 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,7 +133,7 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
134 |
is_retry: isRetry,
|
135 |
is_continue: isContinue,
|
136 |
web_search: webSearch,
|
137 |
-
files:
|
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,44 +146,43 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
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(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
|
165 |
// check sizes
|
166 |
-
|
167 |
-
|
168 |
-
|
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 |
-
|
184 |
-
|
185 |
-
|
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,7 +214,13 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
216 |
// add a children to that sibling, where we can write to
|
217 |
const newUserMessageId = addSibling(
|
218 |
conv,
|
219 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
messageId
|
221 |
);
|
222 |
messageToWriteToId = addChildren(
|
@@ -224,7 +228,6 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
224 |
{
|
225 |
from: "assistant",
|
226 |
content: "",
|
227 |
-
files: hashes,
|
228 |
createdAt: new Date(),
|
229 |
updatedAt: new Date(),
|
230 |
},
|
@@ -250,7 +253,7 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
250 |
{
|
251 |
from: "user",
|
252 |
content: newPrompt ?? "",
|
253 |
-
files:
|
254 |
createdAt: new Date(),
|
255 |
updatedAt: new Date(),
|
256 |
},
|
@@ -411,10 +414,9 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
411 |
}
|
412 |
|
413 |
// inject websearch result & optionally images into the messages
|
414 |
-
const processedMessages =
|
415 |
messagesForPrompt,
|
416 |
messageToWriteTo.webSearch,
|
417 |
-
model.multimodal,
|
418 |
convId
|
419 |
);
|
420 |
|
@@ -429,10 +431,11 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
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,
|
|
|
436 |
})) {
|
437 |
// if not generated_text is here it means the generation is not done
|
438 |
if (!output.generated_text) {
|
|
|
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 |
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 |
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 |
// 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 |
{
|
229 |
from: "assistant",
|
230 |
content: "",
|
|
|
231 |
createdAt: new Date(),
|
232 |
updatedAt: new Date(),
|
233 |
},
|
|
|
253 |
{
|
254 |
from: "user",
|
255 |
content: newPrompt ?? "",
|
256 |
+
files: uploadedFiles,
|
257 |
createdAt: new Date(),
|
258 |
updatedAt: new Date(),
|
259 |
},
|
|
|
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 |
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,
|
438 |
+
isMultimodal: model.multimodal,
|
439 |
})) {
|
440 |
// if not generated_text is here it means the generation is not done
|
441 |
if (!output.generated_text) {
|
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 {
|
43 |
|
44 |
-
return new Response(
|
45 |
headers: {
|
46 |
"Content-Type": mime ?? "application/octet-stream",
|
47 |
},
|
|
|
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 |
},
|