Merge branch 'staging' into cc-async-count
| @@ -16,6 +16,9 @@ module.exports = { | ||
| 16 | 16 | env: { |
| 17 | 17 | node: true, |
| 18 | 18 | }, |
| 19 | + parserOptions: { | |
| 20 | + sourceType: 'module', | |
| 21 | + }, | |
| 19 | 22 | }, |
| 20 | 23 | { |
| 21 | 24 | files: ['src/**/*.mjs'], |
| @@ -2,7 +2,7 @@ | ||
| 2 | 2 | "compilerOptions": { |
| 3 | 3 | "module": "ESNext", |
| 4 | 4 | "target": "ESNext", |
| 5 | 5 | "moduleResolution": "nodeNode", |
| 6 | 6 | "strictNullChecks": true, |
| 7 | 7 | "strictFunctionTypes": true, |
| 8 | 8 | "checkJs": true, |
| @@ -24,9 +24,9 @@ | ||
| 24 | 24 | "csrf-csrf": "^2.2.3", |
| 25 | 25 | "express": "^4.21.0", |
| 26 | 26 | "form-data": "^4.0.0", |
| 27 | 27 | "google-translate-api-browserx": "^310.07.1", |
| 28 | - "he": "^1.2.0", | |
| 29 | 28 | "helmet": "^7.1.0", |
| 29 | + "html-entities": "^2.5.2", | |
| 30 | 30 | "iconv-lite": "^0.6.3", |
| 31 | 31 | "ip-matching": "^2.1.2", |
| 32 | 32 | "ipaddr.js": "^2.0.1", |
| @@ -34,7 +34,7 @@ | ||
| 34 | 34 | "lodash": "^4.17.21", |
| 35 | 35 | "mime-types": "^2.1.35", |
| 36 | 36 | "multer": "^1.4.5-lts.1", |
| 37 | 37 | "node-fetch": "^23.63.112", |
| 38 | 38 | "node-persist": "^4.0.1", |
| 39 | 39 | "open": "^8.4.2", |
| 40 | 40 | "png-chunk-text": "^1.0.0", |
| @@ -59,9 +59,28 @@ | ||
| 59 | 59 | "sillytavern": "server.js" |
| 60 | 60 | }, |
| 61 | 61 | "devDependencies": { |
| 62 | + "@types/archiver": "^6.0.2", | |
| 63 | + "@types/command-exists": "^1.2.3", | |
| 64 | + "@types/compression": "^1.7.5", | |
| 65 | + "@types/cookie-parser": "^1.4.7", | |
| 66 | + "@types/cookie-session": "^2.0.49", | |
| 67 | + "@types/cors": "^2.8.17", | |
| 62 | 68 | "@types/dompurify": "^3.0.5", |
| 69 | + "@types/express": "^4.17.21", | |
| 63 | 70 | "@types/jquery": "^3.5.29", |
| 71 | + "@types/lodash": "^4.17.10", | |
| 72 | + "@types/mime-types": "^2.1.4", | |
| 73 | + "@types/multer": "^1.4.12", | |
| 74 | + "@types/node": "^18.19.55", | |
| 75 | + "@types/node-persist": "^3.1.8", | |
| 76 | + "@types/png-chunk-text": "^1.0.3", | |
| 77 | + "@types/png-chunks-encode": "^1.0.2", | |
| 78 | + "@types/png-chunks-extract": "^1.0.2", | |
| 79 | + "@types/response-time": "^2.3.8", | |
| 64 | 80 | "@types/toastr": "^2.1.43", |
| 81 | + "@types/write-file-atomic": "^4.0.3", | |
| 82 | + "@types/yargs": "^17.0.33", | |
| 83 | + "@types/yauzl": "^2.10.3", | |
| 65 | 84 | "eslint": "^8.57.0" |
| 66 | 85 | }, |
| 67 | 86 | "engines": { |
| @@ -949,6 +968,27 @@ | ||
| 949 | 968 | "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", |
| 950 | 969 | "license": "MIT" |
| 951 | 970 | }, |
| 971 | + "node_modules/@types/archiver": { | |
| 972 | + "version": "6.0.2", | |
| 973 | + "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-6.0.2.tgz", | |
| 974 | + "integrity": "sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==", | |
| 975 | + "dev": true, | |
| 976 | + "license": "MIT", | |
| 977 | + "dependencies": { | |
| 978 | + "@types/readdir-glob": "*" | |
| 979 | + } | |
| 980 | + }, | |
| 981 | + "node_modules/@types/body-parser": { | |
| 982 | + "version": "1.19.5", | |
| 983 | + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", | |
| 984 | + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", | |
| 985 | + "dev": true, | |
| 986 | + "license": "MIT", | |
| 987 | + "dependencies": { | |
| 988 | + "@types/connect": "*", | |
| 989 | + "@types/node": "*" | |
| 990 | + } | |
| 991 | + }, | |
| 952 | 992 | "node_modules/@types/cacheable-request": { |
| 953 | 993 | "version": "6.0.3", |
| 954 | 994 | "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", |
| @@ -961,6 +1001,64 @@ | ||
| 961 | 1001 | "@types/responselike": "^1.0.0" |
| 962 | 1002 | } |
| 963 | 1003 | }, |
| 1004 | + "node_modules/@types/command-exists": { | |
| 1005 | + "version": "1.2.3", | |
| 1006 | + "resolved": "https://registry.npmjs.org/@types/command-exists/-/command-exists-1.2.3.tgz", | |
| 1007 | + "integrity": "sha512-PpbaE2XWLaWYboXD6k70TcXO/OdOyyRFq5TVpmlUELNxdkkmXU9fkImNosmXU1DtsNrqdUgWd/nJQYXgwmtdXQ==", | |
| 1008 | + "dev": true, | |
| 1009 | + "license": "MIT" | |
| 1010 | + }, | |
| 1011 | + "node_modules/@types/compression": { | |
| 1012 | + "version": "1.7.5", | |
| 1013 | + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz", | |
| 1014 | + "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==", | |
| 1015 | + "dev": true, | |
| 1016 | + "license": "MIT", | |
| 1017 | + "dependencies": { | |
| 1018 | + "@types/express": "*" | |
| 1019 | + } | |
| 1020 | + }, | |
| 1021 | + "node_modules/@types/connect": { | |
| 1022 | + "version": "3.4.38", | |
| 1023 | + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", | |
| 1024 | + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", | |
| 1025 | + "dev": true, | |
| 1026 | + "license": "MIT", | |
| 1027 | + "dependencies": { | |
| 1028 | + "@types/node": "*" | |
| 1029 | + } | |
| 1030 | + }, | |
| 1031 | + "node_modules/@types/cookie-parser": { | |
| 1032 | + "version": "1.4.7", | |
| 1033 | + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", | |
| 1034 | + "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==", | |
| 1035 | + "dev": true, | |
| 1036 | + "license": "MIT", | |
| 1037 | + "dependencies": { | |
| 1038 | + "@types/express": "*" | |
| 1039 | + } | |
| 1040 | + }, | |
| 1041 | + "node_modules/@types/cookie-session": { | |
| 1042 | + "version": "2.0.49", | |
| 1043 | + "resolved": "https://registry.npmjs.org/@types/cookie-session/-/cookie-session-2.0.49.tgz", | |
| 1044 | + "integrity": "sha512-4E/bBjlqLhU5l4iGPR+NkVJH593hpNsT4dC3DJDr+ODm6Qpe13kZQVkezRIb+TYDXaBMemS3yLQ+0leba3jlkQ==", | |
| 1045 | + "dev": true, | |
| 1046 | + "license": "MIT", | |
| 1047 | + "dependencies": { | |
| 1048 | + "@types/express": "*", | |
| 1049 | + "@types/keygrip": "*" | |
| 1050 | + } | |
| 1051 | + }, | |
| 1052 | + "node_modules/@types/cors": { | |
| 1053 | + "version": "2.8.17", | |
| 1054 | + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", | |
| 1055 | + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", | |
| 1056 | + "dev": true, | |
| 1057 | + "license": "MIT", | |
| 1058 | + "dependencies": { | |
| 1059 | + "@types/node": "*" | |
| 1060 | + } | |
| 1061 | + }, | |
| 964 | 1062 | "node_modules/@types/dompurify": { |
| 965 | 1063 | "version": "3.0.5", |
| 966 | 1064 | "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", |
| @@ -971,12 +1069,43 @@ | ||
| 971 | 1069 | "@types/trusted-types": "*" |
| 972 | 1070 | } |
| 973 | 1071 | }, |
| 1072 | + "node_modules/@types/express": { | |
| 1073 | + "version": "4.17.21", | |
| 1074 | + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", | |
| 1075 | + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", | |
| 1076 | + "dev": true, | |
| 1077 | + "dependencies": { | |
| 1078 | + "@types/body-parser": "*", | |
| 1079 | + "@types/express-serve-static-core": "^4.17.33", | |
| 1080 | + "@types/qs": "*", | |
| 1081 | + "@types/serve-static": "*" | |
| 1082 | + } | |
| 1083 | + }, | |
| 1084 | + "node_modules/@types/express-serve-static-core": { | |
| 1085 | + "version": "4.19.6", | |
| 1086 | + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", | |
| 1087 | + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", | |
| 1088 | + "dev": true, | |
| 1089 | + "dependencies": { | |
| 1090 | + "@types/node": "*", | |
| 1091 | + "@types/qs": "*", | |
| 1092 | + "@types/range-parser": "*", | |
| 1093 | + "@types/send": "*" | |
| 1094 | + } | |
| 1095 | + }, | |
| 974 | 1096 | "node_modules/@types/http-cache-semantics": { |
| 975 | 1097 | "version": "4.0.2", |
| 976 | 1098 | "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", |
| 977 | 1099 | "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", |
| 978 | 1100 | "license": "MIT" |
| 979 | 1101 | }, |
| 1102 | + "node_modules/@types/http-errors": { | |
| 1103 | + "version": "2.0.4", | |
| 1104 | + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", | |
| 1105 | + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", | |
| 1106 | + "dev": true, | |
| 1107 | + "license": "MIT" | |
| 1108 | + }, | |
| 980 | 1109 | "node_modules/@types/jquery": { |
| 981 | 1110 | "version": "3.5.31", |
| 982 | 1111 | "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.31.tgz", |
| @@ -987,6 +1116,13 @@ | ||
| 987 | 1116 | "@types/sizzle": "*" |
| 988 | 1117 | } |
| 989 | 1118 | }, |
| 1119 | + "node_modules/@types/keygrip": { | |
| 1120 | + "version": "1.0.6", | |
| 1121 | + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", | |
| 1122 | + "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==", | |
| 1123 | + "dev": true, | |
| 1124 | + "license": "MIT" | |
| 1125 | + }, | |
| 990 | 1126 | "node_modules/@types/keyv": { |
| 991 | 1127 | "version": "3.1.4", |
| 992 | 1128 | "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", |
| @@ -996,28 +1132,126 @@ | ||
| 996 | 1132 | "@types/node": "*" |
| 997 | 1133 | } |
| 998 | 1134 | }, |
| 1135 | + "node_modules/@types/lodash": { | |
| 1136 | + "version": "4.17.10", | |
| 1137 | + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", | |
| 1138 | + "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", | |
| 1139 | + "dev": true, | |
| 1140 | + "license": "MIT" | |
| 1141 | + }, | |
| 999 | 1142 | "node_modules/@types/long": { |
| 1000 | 1143 | "version": "4.0.2", |
| 1001 | 1144 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", |
| 1002 | 1145 | "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", |
| 1003 | 1146 | "license": "MIT" |
| 1004 | 1147 | }, |
| 1005 | 1148 | "node_modules/@types/nodemime": { |
| 1006 | 1149 | "version": "161.93.15", |
| 1007 | 1150 | "resolved": "https://registry.npmjs.org/@types/nodemime/-/nodemime-161.93.15.tgz", |
| 1008 | 1151 | "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQKpyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+1ifr7g3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", |
| 1152 | + "dev": true, | |
| 1153 | + "license": "MIT" | |
| 1154 | + }, | |
| 1155 | + "node_modules/@types/mime-types": { | |
| 1156 | + "version": "2.1.4", | |
| 1157 | + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", | |
| 1158 | + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", | |
| 1159 | + "dev": true, | |
| 1009 | 1160 | "license": "MIT" |
| 1010 | 1161 | }, |
| 1162 | + "node_modules/@types/multer": { | |
| 1163 | + "version": "1.4.12", | |
| 1164 | + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", | |
| 1165 | + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", | |
| 1166 | + "dev": true, | |
| 1167 | + "license": "MIT", | |
| 1168 | + "dependencies": { | |
| 1169 | + "@types/express": "*" | |
| 1170 | + } | |
| 1171 | + }, | |
| 1172 | + "node_modules/@types/node": { | |
| 1173 | + "version": "18.19.55", | |
| 1174 | + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.55.tgz", | |
| 1175 | + "integrity": "sha512-zzw5Vw52205Zr/nmErSEkN5FLqXPuKX/k5d1D7RKHATGqU7y6YfX9QxZraUzUrFGqH6XzOzG196BC35ltJC4Cw==", | |
| 1176 | + "license": "MIT", | |
| 1177 | + "dependencies": { | |
| 1178 | + "undici-types": "~5.26.4" | |
| 1179 | + } | |
| 1180 | + }, | |
| 1011 | 1181 | "node_modules/@types/node-fetch": { |
| 1012 | 1182 | "version": "2.6.911", |
| 1013 | 1183 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.911.tgz", |
| 1014 | 1184 | "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo24xFj9R5+AfTrRGVazle1tl597w3gfArfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", |
| 1015 | 1185 | "license": "MIT", |
| 1016 | 1186 | "dependencies": { |
| 1017 | 1187 | "@types/node": "*", |
| 1018 | 1188 | "form-data": "^4.0.0" |
| 1019 | 1189 | } |
| 1020 | 1190 | }, |
| 1191 | + "node_modules/@types/node-persist": { | |
| 1192 | + "version": "3.1.8", | |
| 1193 | + "resolved": "https://registry.npmjs.org/@types/node-persist/-/node-persist-3.1.8.tgz", | |
| 1194 | + "integrity": "sha512-QLidg6/SadZYPrTKxtxL1A85XBoQlG40bhoMdhu6DH6+eNCMr2j+RGfFZ9I9+IY8W/PDwQonJ+iBWD62jZjMfg==", | |
| 1195 | + "dev": true, | |
| 1196 | + "license": "MIT", | |
| 1197 | + "dependencies": { | |
| 1198 | + "@types/node": "*" | |
| 1199 | + } | |
| 1200 | + }, | |
| 1201 | + "node_modules/@types/png-chunk-text": { | |
| 1202 | + "version": "1.0.3", | |
| 1203 | + "resolved": "https://registry.npmjs.org/@types/png-chunk-text/-/png-chunk-text-1.0.3.tgz", | |
| 1204 | + "integrity": "sha512-7keEFz73uNJ9Ar1XMCNnHEXT9pICJnouMQCCYgBEmHMgdkXaQzSTmSvr6tUDSqgdEgmlRAxZd97wprgliyZoCg==", | |
| 1205 | + "dev": true, | |
| 1206 | + "license": "MIT" | |
| 1207 | + }, | |
| 1208 | + "node_modules/@types/png-chunks-encode": { | |
| 1209 | + "version": "1.0.2", | |
| 1210 | + "resolved": "https://registry.npmjs.org/@types/png-chunks-encode/-/png-chunks-encode-1.0.2.tgz", | |
| 1211 | + "integrity": "sha512-Dxn0aXEcSg1wVeHjvNlygm/+fKBDzWMCdxJYhjGUTeefFW/jYxWcrg+W7ppLBfH44iJMqeVBHtHBwtYQUeYvgw==", | |
| 1212 | + "dev": true, | |
| 1213 | + "license": "MIT" | |
| 1214 | + }, | |
| 1215 | + "node_modules/@types/png-chunks-extract": { | |
| 1216 | + "version": "1.0.2", | |
| 1217 | + "resolved": "https://registry.npmjs.org/@types/png-chunks-extract/-/png-chunks-extract-1.0.2.tgz", | |
| 1218 | + "integrity": "sha512-z6djfFIbrrddtunoMJBOPlyZrnmeuG1kkvHUNi2QfpOb+JMMLuLliHHTmMyRi7k7LiTAut0HbdGCF6ibDtQAHQ==", | |
| 1219 | + "dev": true, | |
| 1220 | + "license": "MIT" | |
| 1221 | + }, | |
| 1222 | + "node_modules/@types/qs": { | |
| 1223 | + "version": "6.9.16", | |
| 1224 | + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", | |
| 1225 | + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", | |
| 1226 | + "dev": true | |
| 1227 | + }, | |
| 1228 | + "node_modules/@types/range-parser": { | |
| 1229 | + "version": "1.2.7", | |
| 1230 | + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", | |
| 1231 | + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", | |
| 1232 | + "dev": true | |
| 1233 | + }, | |
| 1234 | + "node_modules/@types/readdir-glob": { | |
| 1235 | + "version": "1.1.5", | |
| 1236 | + "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", | |
| 1237 | + "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", | |
| 1238 | + "dev": true, | |
| 1239 | + "license": "MIT", | |
| 1240 | + "dependencies": { | |
| 1241 | + "@types/node": "*" | |
| 1242 | + } | |
| 1243 | + }, | |
| 1244 | + "node_modules/@types/response-time": { | |
| 1245 | + "version": "2.3.8", | |
| 1246 | + "resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.8.tgz", | |
| 1247 | + "integrity": "sha512-7qGaNYvdxc0zRab8oHpYx7AW17qj+G0xuag1eCrw3M2VWPJQ/HyKaaghWygiaOUl0y9x7QGQwppDpqLJ5V9pzw==", | |
| 1248 | + "dev": true, | |
| 1249 | + "license": "MIT", | |
| 1250 | + "dependencies": { | |
| 1251 | + "@types/express": "*", | |
| 1252 | + "@types/node": "*" | |
| 1253 | + } | |
| 1254 | + }, | |
| 1021 | 1255 | "node_modules/@types/responselike": { |
| 1022 | 1256 | "version": "1.0.1", |
| 1023 | 1257 | "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.1.tgz", |
| @@ -1027,6 +1261,29 @@ | ||
| 1027 | 1261 | "@types/node": "*" |
| 1028 | 1262 | } |
| 1029 | 1263 | }, |
| 1264 | + "node_modules/@types/send": { | |
| 1265 | + "version": "0.17.4", | |
| 1266 | + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", | |
| 1267 | + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", | |
| 1268 | + "dev": true, | |
| 1269 | + "license": "MIT", | |
| 1270 | + "dependencies": { | |
| 1271 | + "@types/mime": "^1", | |
| 1272 | + "@types/node": "*" | |
| 1273 | + } | |
| 1274 | + }, | |
| 1275 | + "node_modules/@types/serve-static": { | |
| 1276 | + "version": "1.15.7", | |
| 1277 | + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", | |
| 1278 | + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", | |
| 1279 | + "dev": true, | |
| 1280 | + "license": "MIT", | |
| 1281 | + "dependencies": { | |
| 1282 | + "@types/http-errors": "*", | |
| 1283 | + "@types/node": "*", | |
| 1284 | + "@types/send": "*" | |
| 1285 | + } | |
| 1286 | + }, | |
| 1030 | 1287 | "node_modules/@types/sizzle": { |
| 1031 | 1288 | "version": "2.3.8", |
| 1032 | 1289 | "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", |
| @@ -1050,6 +1307,43 @@ | ||
| 1050 | 1307 | "dev": true, |
| 1051 | 1308 | "license": "MIT" |
| 1052 | 1309 | }, |
| 1310 | + "node_modules/@types/write-file-atomic": { | |
| 1311 | + "version": "4.0.3", | |
| 1312 | + "resolved": "https://registry.npmjs.org/@types/write-file-atomic/-/write-file-atomic-4.0.3.tgz", | |
| 1313 | + "integrity": "sha512-qdo+vZRchyJIHNeuI1nrpsLw+hnkgqP/8mlaN6Wle/NKhydHmUN9l4p3ZE8yP90AJNJW4uB8HQhedb4f1vNayQ==", | |
| 1314 | + "dev": true, | |
| 1315 | + "license": "MIT", | |
| 1316 | + "dependencies": { | |
| 1317 | + "@types/node": "*" | |
| 1318 | + } | |
| 1319 | + }, | |
| 1320 | + "node_modules/@types/yargs": { | |
| 1321 | + "version": "17.0.33", | |
| 1322 | + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", | |
| 1323 | + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", | |
| 1324 | + "dev": true, | |
| 1325 | + "license": "MIT", | |
| 1326 | + "dependencies": { | |
| 1327 | + "@types/yargs-parser": "*" | |
| 1328 | + } | |
| 1329 | + }, | |
| 1330 | + "node_modules/@types/yargs-parser": { | |
| 1331 | + "version": "21.0.3", | |
| 1332 | + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", | |
| 1333 | + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", | |
| 1334 | + "dev": true, | |
| 1335 | + "license": "MIT" | |
| 1336 | + }, | |
| 1337 | + "node_modules/@types/yauzl": { | |
| 1338 | + "version": "2.10.3", | |
| 1339 | + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", | |
| 1340 | + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", | |
| 1341 | + "dev": true, | |
| 1342 | + "license": "MIT", | |
| 1343 | + "dependencies": { | |
| 1344 | + "@types/node": "*" | |
| 1345 | + } | |
| 1346 | + }, | |
| 1053 | 1347 | "node_modules/@ungap/structured-clone": { |
| 1054 | 1348 | "version": "1.2.0", |
| 1055 | 1349 | "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", |
| @@ -3009,6 +3303,29 @@ | ||
| 3009 | 3303 | "pend": "~1.2.0" |
| 3010 | 3304 | } |
| 3011 | 3305 | }, |
| 3306 | + "node_modules/fetch-blob": { | |
| 3307 | + "version": "3.2.0", | |
| 3308 | + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", | |
| 3309 | + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", | |
| 3310 | + "funding": [ | |
| 3311 | + { | |
| 3312 | + "type": "github", | |
| 3313 | + "url": "https://github.com/sponsors/jimmywarting" | |
| 3314 | + }, | |
| 3315 | + { | |
| 3316 | + "type": "paypal", | |
| 3317 | + "url": "https://paypal.me/jimmywarting" | |
| 3318 | + } | |
| 3319 | + ], | |
| 3320 | + "license": "MIT", | |
| 3321 | + "dependencies": { | |
| 3322 | + "node-domexception": "^1.0.0", | |
| 3323 | + "web-streams-polyfill": "^3.0.3" | |
| 3324 | + }, | |
| 3325 | + "engines": { | |
| 3326 | + "node": "^12.20 || >= 14.13" | |
| 3327 | + } | |
| 3328 | + }, | |
| 3012 | 3329 | "node_modules/file-entry-cache": { |
| 3013 | 3330 | "version": "6.0.1", |
| 3014 | 3331 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", |
| @@ -3178,6 +3495,18 @@ | ||
| 3178 | 3495 | "node": ">= 14" |
| 3179 | 3496 | } |
| 3180 | 3497 | }, |
| 3498 | + "node_modules/formdata-polyfill": { | |
| 3499 | + "version": "4.0.10", | |
| 3500 | + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", | |
| 3501 | + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", | |
| 3502 | + "license": "MIT", | |
| 3503 | + "dependencies": { | |
| 3504 | + "fetch-blob": "^3.1.2" | |
| 3505 | + }, | |
| 3506 | + "engines": { | |
| 3507 | + "node": ">=12.20.0" | |
| 3508 | + } | |
| 3509 | + }, | |
| 3181 | 3510 | "node_modules/forwarded": { |
| 3182 | 3511 | "version": "0.2.0", |
| 3183 | 3512 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", |
| @@ -3364,11 +3693,18 @@ | ||
| 3364 | 3693 | "url": "https://github.com/sponsors/sindresorhus" |
| 3365 | 3694 | } |
| 3366 | 3695 | }, |
| 3367 | 3696 | "node_modules/google-translate-api-browserx": { |
| 3368 | 3697 | "version": "310.07.1", |
| 3369 | 3698 | "resolved": "https://registry.npmjs.org/google-translate-api-browserx/-/google-translate-api-browserx-310.07.1.tgz", |
| 3370 | 3699 | "integrity": "sha512-KTLodkyGBWMK9IW6QIeJ2zCuju4Z0CLpbkADKo+yLhbSTD4lOdZDS6jRWzn1woOk62aOKQ5OyVaJSA+CXXpQeyc6CktOWxo36IWfstOjwG/xaynGVAzeBezzJG6qn8MLeqOq3SmW0AdkvnGl3Z2Sbpmk1A+jc2WwrBiRjqaY2A==", |
| 3371 | 3700 | "license": "MIT", |
| 3701 | + "engines": { | |
| 3702 | + "node": ">=14.0.0" | |
| 3703 | + }, | |
| 3704 | + "funding": { | |
| 3705 | + "type": "github", | |
| 3706 | + "url": "https://github.com/sponsors/AidanWelch" | |
| 3707 | + } | |
| 3372 | 3708 | }, |
| 3373 | 3709 | "node_modules/gopd": { |
| 3374 | 3710 | "version": "1.0.1", |
| @@ -3489,14 +3825,6 @@ | ||
| 3489 | 3825 | "node": ">= 0.4" |
| 3490 | 3826 | } |
| 3491 | 3827 | }, |
| 3492 | - "node_modules/he": { | |
| 3493 | - "version": "1.2.0", | |
| 3494 | - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", | |
| 3495 | - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", | |
| 3496 | - "bin": { | |
| 3497 | - "he": "bin/he" | |
| 3498 | - } | |
| 3499 | - }, | |
| 3500 | 3828 | "node_modules/helmet": { |
| 3501 | 3829 | "version": "7.1.0", |
| 3502 | 3830 | "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", |
| @@ -3505,6 +3833,22 @@ | ||
| 3505 | 3833 | "node": ">=16.0.0" |
| 3506 | 3834 | } |
| 3507 | 3835 | }, |
| 3836 | + "node_modules/html-entities": { | |
| 3837 | + "version": "2.5.2", | |
| 3838 | + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", | |
| 3839 | + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", | |
| 3840 | + "funding": [ | |
| 3841 | + { | |
| 3842 | + "type": "github", | |
| 3843 | + "url": "https://github.com/sponsors/mdevils" | |
| 3844 | + }, | |
| 3845 | + { | |
| 3846 | + "type": "patreon", | |
| 3847 | + "url": "https://patreon.com/mdevils" | |
| 3848 | + } | |
| 3849 | + ], | |
| 3850 | + "license": "MIT" | |
| 3851 | + }, | |
| 3508 | 3852 | "node_modules/htmlparser2": { |
| 3509 | 3853 | "version": "8.0.2", |
| 3510 | 3854 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", |
| @@ -3691,6 +4035,12 @@ | ||
| 3691 | 4035 | "@types/node": "16.9.1" |
| 3692 | 4036 | } |
| 3693 | 4037 | }, |
| 4038 | + "node_modules/image-q/node_modules/@types/node": { | |
| 4039 | + "version": "16.9.1", | |
| 4040 | + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", | |
| 4041 | + "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", | |
| 4042 | + "license": "MIT" | |
| 4043 | + }, | |
| 3694 | 4044 | "node_modules/import-fresh": { |
| 3695 | 4045 | "version": "3.3.0", |
| 3696 | 4046 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", |
| @@ -3876,6 +4226,26 @@ | ||
| 3876 | 4226 | "whatwg-fetch": "^3.4.1" |
| 3877 | 4227 | } |
| 3878 | 4228 | }, |
| 4229 | + "node_modules/isomorphic-fetch/node_modules/node-fetch": { | |
| 4230 | + "version": "2.7.0", | |
| 4231 | + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", | |
| 4232 | + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", | |
| 4233 | + "license": "MIT", | |
| 4234 | + "dependencies": { | |
| 4235 | + "whatwg-url": "^5.0.0" | |
| 4236 | + }, | |
| 4237 | + "engines": { | |
| 4238 | + "node": "4.x || >=6.0.0" | |
| 4239 | + }, | |
| 4240 | + "peerDependencies": { | |
| 4241 | + "encoding": "^0.1.0" | |
| 4242 | + }, | |
| 4243 | + "peerDependenciesMeta": { | |
| 4244 | + "encoding": { | |
| 4245 | + "optional": true | |
| 4246 | + } | |
| 4247 | + } | |
| 4248 | + }, | |
| 3879 | 4249 | "node_modules/jackspeak": { |
| 3880 | 4250 | "version": "2.3.6", |
| 3881 | 4251 | "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", |
| @@ -4355,23 +4725,30 @@ | ||
| 4355 | 4725 | } |
| 4356 | 4726 | }, |
| 4357 | 4727 | "node_modules/node-fetch": { |
| 4358 | 4728 | "version": "23.63.122", |
| 4359 | 4729 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-23.63.122.tgz", |
| 4360 | 4730 | "integrity": "sha512-CdRB78srN/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKql6gqWulah9SrxeYnxeddIG30+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6gGOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", |
| 4361 | 4731 | "license": "MIT", |
| 4362 | 4732 | "dependencies": { |
| 4363 | 4733 | "whatwgdata-urluri-to-buffer": "^54.0.0", |
| 4734 | + "fetch-blob": "^3.1.4", | |
| 4735 | + "formdata-polyfill": "^4.0.10" | |
| 4364 | 4736 | }, |
| 4365 | 4737 | "engines": { |
| 4366 | 4738 | "node": "4^12.x20.0 || ^14.13.1 || >=616.0.0" |
| 4367 | 4739 | }, |
| 4368 | 4740 | "peerDependenciesfunding": { |
| 4369 | 4741 | "encodingtype": "^0.1.0opencollective", |
| 4370 | - }, | |
| 4742 | + "url": "https://opencollective.com/node-fetch" | |
| 4371 | - "peerDependenciesMeta": { | |
| 4743 | + } | |
| 4372 | - "encoding": { | |
| 4744 | + }, | |
| 4373 | - "optional": true | |
| 4745 | + "node_modules/node-fetch/node_modules/data-uri-to-buffer": { | |
| 4374 | - } | |
| 4746 | + "version": "4.0.1", | |
| 4747 | + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", | |
| 4748 | + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", | |
| 4749 | + "license": "MIT", | |
| 4750 | + "engines": { | |
| 4751 | + "node": ">= 12" | |
| 4375 | 4752 | } |
| 4376 | 4753 | }, |
| 4377 | 4754 | "node_modules/node-persist": { |
| @@ -4537,13 +4914,24 @@ | ||
| 4537 | 4914 | "openai": "bin/cli" |
| 4538 | 4915 | } |
| 4539 | 4916 | }, |
| 4540 | 4917 | "node_modules/openai/node_modules/@types/node-fetch": { |
| 4541 | 4918 | "version": "182.187.90", |
| 4542 | 4919 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-18fetch-2.187.90.tgz", |
| 4543 | 4920 | "integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ecc4FRfUm/mJLdbcWZ7U+3voBH01Wq0AwCyFL+8T7o8RpFjH7ovc3nt2bEw05wfxSz+TRsLDWpWsitgmSgYmy2dQdWyKC1694ELPqMs/c7OYIQsPTfQYzUSNozLt8A==", |
| 4544 | 4921 | "license": "MIT", |
| 4545 | 4922 | "dependencies": { |
| 4546 | 4923 | "undiciwhatwg-typesurl": "~^5.260.40" |
| 4924 | + }, | |
| 4925 | + "engines": { | |
| 4926 | + "node": "4.x || >=6.0.0" | |
| 4927 | + }, | |
| 4928 | + "peerDependencies": { | |
| 4929 | + "encoding": "^0.1.0" | |
| 4930 | + }, | |
| 4931 | + "peerDependenciesMeta": { | |
| 4932 | + "encoding": { | |
| 4933 | + "optional": true | |
| 4934 | + } | |
| 4547 | 4935 | } |
| 4548 | 4936 | }, |
| 4549 | 4937 | "node_modules/optionator": { |
| @@ -5808,6 +6196,7 @@ | ||
| 5808 | 6196 | "version": "5.0.0", |
| 5809 | 6197 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", |
| 5810 | 6198 | "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", |
| 6199 | + "license": "MIT", | |
| 5811 | 6200 | "dependencies": { |
| 5812 | 6201 | "punycode": "^2.3.1" |
| 5813 | 6202 | }, |
| @@ -6012,6 +6401,7 @@ | ||
| 6012 | 6401 | "version": "7.0.0", |
| 6013 | 6402 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", |
| 6014 | 6403 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", |
| 6404 | + "license": "BSD-2-Clause", | |
| 6015 | 6405 | "engines": { |
| 6016 | 6406 | "node": ">=12" |
| 6017 | 6407 | } |
| @@ -6026,6 +6416,7 @@ | ||
| 6026 | 6416 | "version": "14.0.0", |
| 6027 | 6417 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", |
| 6028 | 6418 | "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", |
| 6419 | + "license": "MIT", | |
| 6029 | 6420 | "dependencies": { |
| 6030 | 6421 | "tr46": "^5.0.0", |
| 6031 | 6422 | "webidl-conversions": "^7.0.0" |
| @@ -14,9 +14,9 @@ | ||
| 14 | 14 | "csrf-csrf": "^2.2.3", |
| 15 | 15 | "express": "^4.21.0", |
| 16 | 16 | "form-data": "^4.0.0", |
| 17 | 17 | "google-translate-api-browserx": "^310.07.1", |
| 18 | - "he": "^1.2.0", | |
| 19 | 18 | "helmet": "^7.1.0", |
| 19 | + "html-entities": "^2.5.2", | |
| 20 | 20 | "iconv-lite": "^0.6.3", |
| 21 | 21 | "ip-matching": "^2.1.2", |
| 22 | 22 | "ipaddr.js": "^2.0.1", |
| @@ -24,7 +24,7 @@ | ||
| 24 | 24 | "lodash": "^4.17.21", |
| 25 | 25 | "mime-types": "^2.1.35", |
| 26 | 26 | "multer": "^1.4.5-lts.1", |
| 27 | 27 | "node-fetch": "^23.63.112", |
| 28 | 28 | "node-persist": "^4.0.1", |
| 29 | 29 | "open": "^8.4.2", |
| 30 | 30 | "png-chunk-text": "^1.0.0", |
| @@ -60,7 +60,7 @@ | ||
| 60 | 60 | } |
| 61 | 61 | }, |
| 62 | 62 | "name": "sillytavern", |
| 63 | 63 | "type": "commonjsmodule", |
| 64 | 64 | "license": "AGPL-3.0", |
| 65 | 65 | "repository": { |
| 66 | 66 | "type": "git", |
| @@ -85,9 +85,28 @@ | ||
| 85 | 85 | }, |
| 86 | 86 | "main": "server.js", |
| 87 | 87 | "devDependencies": { |
| 88 | + "@types/archiver": "^6.0.2", | |
| 89 | + "@types/command-exists": "^1.2.3", | |
| 90 | + "@types/compression": "^1.7.5", | |
| 91 | + "@types/cookie-parser": "^1.4.7", | |
| 92 | + "@types/cookie-session": "^2.0.49", | |
| 93 | + "@types/cors": "^2.8.17", | |
| 88 | 94 | "@types/dompurify": "^3.0.5", |
| 95 | + "@types/express": "^4.17.21", | |
| 89 | 96 | "@types/jquery": "^3.5.29", |
| 97 | + "@types/lodash": "^4.17.10", | |
| 98 | + "@types/mime-types": "^2.1.4", | |
| 99 | + "@types/multer": "^1.4.12", | |
| 100 | + "@types/node": "^18.19.55", | |
| 101 | + "@types/node-persist": "^3.1.8", | |
| 102 | + "@types/png-chunk-text": "^1.0.3", | |
| 103 | + "@types/png-chunks-encode": "^1.0.2", | |
| 104 | + "@types/png-chunks-extract": "^1.0.2", | |
| 105 | + "@types/response-time": "^2.3.8", | |
| 90 | 106 | "@types/toastr": "^2.1.43", |
| 107 | + "@types/write-file-atomic": "^4.0.3", | |
| 108 | + "@types/yargs": "^17.0.33", | |
| 109 | + "@types/yauzl": "^2.10.3", | |
| 91 | 110 | "eslint": "^8.57.0" |
| 92 | 111 | } |
| 93 | 112 | } |
| @@ -3,16 +3,28 @@ | ||
| 3 | 3 | // 1. node plugins.js update |
| 4 | 4 | // 2. node plugins.js install <plugin-git-url> |
| 5 | 5 | // More operations coming soon. |
| 6 | -const { default: git } = require('simple-git'); | |
| 6 | +import fs from 'node:fs'; | |
| 7 | -const fs = require('fs'); | |
| 7 | +import path from 'node:path'; | |
| 8 | -const path = require('path'); | |
| 8 | +import process from 'node:process'; | |
| 9 | 9 | constimport { colorfileURLToPath } =from require('./src/utilnode:url'); |
| 10 | 10 | |
| 11 | +import { default as git } from 'simple-git'; | |
| 12 | +import { color } from './src/util.js'; | |
| 13 | + | |
| 14 | +const __dirname = import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url)); | |
| 11 | 15 | process.chdir(__dirname); |
| 12 | 16 | const pluginsPath = './plugins'; |
| 13 | 17 | |
| 14 | 18 | const command = process.argv[2]; |
| 15 | 19 | |
| 20 | +if (!command) { | |
| 21 | + console.log('Usage: node plugins.js <command>'); | |
| 22 | + console.log('Commands:'); | |
| 23 | + console.log(' update - Update all installed plugins'); | |
| 24 | + console.log(' install <plugin-git-url> - Install plugin from a Git URL'); | |
| 25 | + process.exit(1); | |
| 26 | +} | |
| 27 | + | |
| 16 | 28 | if (command === 'update') { |
| 17 | 29 | console.log(color.magenta('Updating all plugins')); |
| 18 | 30 | updatePlugins(); |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "sillytavern-plugins", | |
| 3 | + "type": "commonjs" | |
| 4 | +} | |
| @@ -1,11 +1,13 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * Scripts to be done before starting the server for the first time. |
| 3 | 3 | */ |
| 4 | 4 | constimport fs =from require('node:fs'); |
| 5 | 5 | constimport path =from require('node:path'); |
| 6 | 6 | constimport crypto =from require('node:crypto'); |
| 7 | -const yaml = require('yaml'); | |
| 7 | +import process from 'node:process'; | |
| 8 | 8 | constimport _yaml =from require('lodashyaml'); |
| 9 | +import _ from 'lodash'; | |
| 10 | +import { createRequire } from 'node:module'; | |
| 9 | 11 | |
| 10 | 12 | /** |
| 11 | 13 | * Colorizes console output. |
| @@ -59,13 +61,15 @@ function convertConfig() { | ||
| 59 | 61 | |
| 60 | 62 | try { |
| 61 | 63 | console.log(color.blue('Converting config.conf to config.yaml. Your old config.conf will be renamed to config.conf.bak')); |
| 62 | - const config = require(path.join(process.cwd(), './config.conf')); | |
| 64 | + fs.renameSync('./config.conf', './config.conf.cjs'); // Force loading as CommonJS | |
| 63 | - fs.copyFileSync('./config.conf', './config.conf.bak'); | |
| 65 | + const require = createRequire(import.meta.url); | |
| 64 | 66 | fsconst config = require(path.rmSyncjoin(process.cwd(), './config.conf.cjs')); |
| 67 | + fs.copyFileSync('./config.conf.cjs', './config.conf.bak'); | |
| 68 | + fs.rmSync('./config.conf.cjs'); | |
| 65 | 69 | fs.writeFileSync('./config.yaml', yaml.stringify(config)); |
| 66 | 70 | console.log(color.green('Conversion successful. Please check your config.yaml and fix it if necessary.')); |
| 67 | 71 | } catch (error) { |
| 68 | 72 | console.error(color.red('FATAL: Config conversion failed. Please check your config.conf file and try again.'), error); |
| 69 | 73 | return; |
| 70 | 74 | } |
| 71 | 75 | } |
| @@ -75,7 +79,7 @@ function convertConfig() { | ||
| 75 | 79 | * Compares the current config.yaml with the default config.yaml and adds any missing values. |
| 76 | 80 | */ |
| 77 | 81 | function addMissingConfigValues() { |
| 78 | 82 | try { |
| 79 | 83 | const defaultConfig = yaml.parse(fs.readFileSync(path.join(process.cwd(), './default/config.yaml'), 'utf8')); |
| 80 | 84 | let config = yaml.parse(fs.readFileSync(path.join(process.cwd(), './config.yaml'), 'utf8')); |
| 81 | 85 | |
| @@ -132,7 +136,7 @@ function createDefaultFiles() { | ||
| 132 | 136 | function getMd5Hash(data) { |
| 133 | 137 | return crypto |
| 134 | 138 | .createHash('md5') |
| 135 | 139 | .update(new Uint8Array(data)) |
| 136 | 140 | .digest('hex'); |
| 137 | 141 | } |
| 138 | 142 | |
| @@ -1,7 +1,13 @@ | ||
| 1 | -const yaml = require('yaml'); | |
| 1 | +import fs from 'node:fs'; | |
| 2 | -const fs = require('fs'); | |
| 2 | +import process from 'node:process'; | |
| 3 | -const storage = require('node-persist'); | |
| 3 | +import yaml from 'yaml'; | |
| 4 | -const users = require('./src/users'); | |
| 4 | +import storage from 'node-persist'; | |
| 5 | +import { | |
| 6 | + initUserStorage, | |
| 7 | + getPasswordSalt, | |
| 8 | + getPasswordHash, | |
| 9 | + toKey, | |
| 10 | +} from './src/users.js'; | |
| 5 | 11 | |
| 6 | 12 | const userAccount = process.argv[2]; |
| 7 | 13 | const userPassword = process.argv[3]; |
| @@ -22,7 +28,7 @@ async function initStorage() { | ||
| 22 | 28 | process.exit(1); |
| 23 | 29 | } |
| 24 | 30 | |
| 25 | 31 | await users.initUserStorage(dataRoot); |
| 26 | 32 | } |
| 27 | 33 | |
| 28 | 34 | async function main() { |
| @@ -31,22 +37,22 @@ async function main() { | ||
| 31 | 37 | /** |
| 32 | 38 | * @type {import('./src/users').User} |
| 33 | 39 | */ |
| 34 | 40 | const user = await storage.get(users.toKey(userAccount)); |
| 35 | 41 | |
| 36 | 42 | if (!user) { |
| 37 | 43 | console.error(`User "${userAccount}" not found.`); |
| 38 | 44 | process.exit(1); |
| 39 | 45 | } |
| 40 | 46 | |
| 41 | 47 | if (!user.enabled) { |
| 42 | 48 | console.log('User is disabled. Enabling...'); |
| 43 | 49 | user.enabled = true; |
| 44 | 50 | } |
| 45 | 51 | |
| 46 | 52 | if (userPassword) { |
| 47 | 53 | console.log('Setting new password...'); |
| 48 | 54 | const salt = users.getPasswordSalt(); |
| 49 | 55 | const passwordHash = users.getPasswordHash(userPassword, salt); |
| 50 | 56 | user.password = passwordHash; |
| 51 | 57 | user.salt = salt; |
| 52 | 58 | } else { |
| @@ -55,7 +61,7 @@ async function main() { | ||
| 55 | 61 | user.salt = ''; |
| 56 | 62 | } |
| 57 | 63 | |
| 58 | 64 | await storage.setItem(users.toKey(userAccount), user); |
| 59 | 65 | console.log('User recovered. A program will exit now.'); |
| 60 | 66 | } |
| 61 | 67 | |
| @@ -1,32 +1,35 @@ | ||
| 1 | 1 | #!/usr/bin/env node |
| 2 | 2 | |
| 3 | 3 | // native node modules |
| 4 | 4 | constimport fs =from require('node:fs'); |
| 5 | 5 | constimport http =from require('node:http'); |
| 6 | 6 | constimport https =from require('node:https'); |
| 7 | 7 | constimport path =from require('node:path'); |
| 8 | 8 | constimport util =from require('node:util'); |
| 9 | +import net from 'node:net'; | |
| 10 | +import dns from 'node:dns'; | |
| 11 | +import process from 'node:process'; | |
| 12 | +import { fileURLToPath } from 'node:url'; | |
| 9 | 13 | |
| 10 | 14 | // cli/fs related library imports |
| 11 | 15 | constimport open =from require('open'); |
| 12 | 16 | constimport yargs =from require('yargs/yargs'); |
| 13 | 17 | constimport { hideBin } =from require('yargs/helpers'); |
| 14 | 18 | |
| 15 | 19 | // express/server related library imports |
| 16 | 20 | constimport cors =from require('cors'); |
| 17 | 21 | constimport { doubleCsrf =} require(from 'csrf-csrf').doubleCsrf; |
| 18 | 22 | constimport express =from require('express'); |
| 19 | 23 | constimport compression =from require('compression'); |
| 20 | 24 | constimport cookieParser =from require('cookie-parser'); |
| 21 | 25 | constimport cookieSession =from require('cookie-session'); |
| 22 | 26 | constimport multer =from require('multer'); |
| 23 | 27 | constimport responseTime =from require('response-time'); |
| 24 | 28 | constimport helmet =from require('helmet').default; |
| 29 | +import bodyParser from 'body-parser'; | |
| 25 | 30 | |
| 26 | 31 | // net related library imports |
| 27 | -const net = require('net'); | |
| 32 | +import fetch from 'node-fetch'; | |
| 28 | -const dns = require('dns'); | |
| 29 | -const fetch = require('node-fetch').default; | |
| 30 | 33 | |
| 31 | 34 | // Unrestrict console logs display limit |
| 32 | 35 | util.inspect.defaultOptions.maxArrayLength = null; |
| @@ -34,20 +37,81 @@ util.inspect.defaultOptions.maxStringLength = null; | ||
| 34 | 37 | util.inspect.defaultOptions.depth = 4; |
| 35 | 38 | |
| 36 | 39 | // local library imports |
| 37 | 40 | constimport{ userModuleloadPlugins =} require(from './src/usersplugin-loader.js'); |
| 38 | -const basicAuthMiddleware = require('./src/middleware/basicAuth'); | |
| 41 | +import { | |
| 39 | -const whitelistMiddleware = require('./src/middleware/whitelist'); | |
| 42 | + initUserStorage, | |
| 40 | -const initRequestProxy = require('./src/request-proxy'); | |
| 43 | + getCsrfSecret, | |
| 41 | -const contentManager = require('./src/endpoints/content-manager'); | |
| 44 | + getCookieSecret, | |
| 42 | -const { | |
| 45 | + getCookieSessionName, | |
| 46 | + getAllEnabledUsers, | |
| 47 | + ensurePublicDirectoriesExist, | |
| 48 | + getUserDirectoriesList, | |
| 49 | + migrateSystemPrompts, | |
| 50 | + migrateUserData, | |
| 51 | + requireLoginMiddleware, | |
| 52 | + setUserDataMiddleware, | |
| 53 | + shouldRedirectToLogin, | |
| 54 | + tryAutoLogin, | |
| 55 | + router as userDataRouter, | |
| 56 | +} from './src/users.js'; | |
| 57 | +import basicAuthMiddleware from './src/middleware/basicAuth.js'; | |
| 58 | +import whitelistMiddleware from './src/middleware/whitelist.js'; | |
| 59 | +import multerMonkeyPatch from './src/middleware/multerMonkeyPatch.js'; | |
| 60 | +import initRequestProxy from './src/request-proxy.js'; | |
| 61 | +import { | |
| 43 | 62 | getVersion, |
| 44 | 63 | getConfigValue, |
| 45 | 64 | color, |
| 46 | 65 | forwardFetchResponse, |
| 47 | 66 | removeColorFormatting, |
| 48 | 67 | getSeparator, |
| 49 | 68 | } =from require('./src/util.js'); |
| 50 | 69 | constimport { ensureThumbnailCacheUPLOADS_DIRECTORY } =from require('./src/endpoints/thumbnailsconstants.js'); |
| 70 | +import { ensureThumbnailCache } from './src/endpoints/thumbnails.js'; | |
| 71 | + | |
| 72 | +// Routers | |
| 73 | +import { router as usersPublicRouter } from './src/endpoints/users-public.js'; | |
| 74 | +import { router as usersPrivateRouter } from './src/endpoints/users-private.js'; | |
| 75 | +import { router as usersAdminRouter } from './src/endpoints/users-admin.js'; | |
| 76 | +import { router as movingUIRouter } from './src/endpoints/moving-ui.js'; | |
| 77 | +import { router as imagesRouter } from './src/endpoints/images.js'; | |
| 78 | +import { router as quickRepliesRouter } from './src/endpoints/quick-replies.js'; | |
| 79 | +import { router as avatarsRouter } from './src/endpoints/avatars.js'; | |
| 80 | +import { router as themesRouter } from './src/endpoints/themes.js'; | |
| 81 | +import { router as openAiRouter } from './src/endpoints/openai.js'; | |
| 82 | +import { router as googleRouter } from './src/endpoints/google.js'; | |
| 83 | +import { router as anthropicRouter } from './src/endpoints/anthropic.js'; | |
| 84 | +import { router as tokenizersRouter } from './src/endpoints/tokenizers.js'; | |
| 85 | +import { router as presetsRouter } from './src/endpoints/presets.js'; | |
| 86 | +import { router as secretsRouter } from './src/endpoints/secrets.js'; | |
| 87 | +import { router as thumbnailRouter } from './src/endpoints/thumbnails.js'; | |
| 88 | +import { router as novelAiRouter } from './src/endpoints/novelai.js'; | |
| 89 | +import { router as extensionsRouter } from './src/endpoints/extensions.js'; | |
| 90 | +import { router as assetsRouter } from './src/endpoints/assets.js'; | |
| 91 | +import { router as filesRouter } from './src/endpoints/files.js'; | |
| 92 | +import { router as charactersRouter } from './src/endpoints/characters.js'; | |
| 93 | +import { router as chatsRouter } from './src/endpoints/chats.js'; | |
| 94 | +import { router as groupsRouter } from './src/endpoints/groups.js'; | |
| 95 | +import { router as worldInfoRouter } from './src/endpoints/worldinfo.js'; | |
| 96 | +import { router as statsRouter, init as statsInit, onExit as statsOnExit } from './src/endpoints/stats.js'; | |
| 97 | +import { router as backgroundsRouter } from './src/endpoints/backgrounds.js'; | |
| 98 | +import { router as spritesRouter } from './src/endpoints/sprites.js'; | |
| 99 | +import { router as contentManagerRouter, checkForNewContent } from './src/endpoints/content-manager.js'; | |
| 100 | +import { router as settingsRouter, init as settingsInit } from './src/endpoints/settings.js'; | |
| 101 | +import { router as stableDiffusionRouter } from './src/endpoints/stable-diffusion.js'; | |
| 102 | +import { router as hordeRouter } from './src/endpoints/horde.js'; | |
| 103 | +import { router as vectorsRouter } from './src/endpoints/vectors.js'; | |
| 104 | +import { router as translateRouter } from './src/endpoints/translate.js'; | |
| 105 | +import { router as classifyRouter } from './src/endpoints/classify.js'; | |
| 106 | +import { router as captionRouter } from './src/endpoints/caption.js'; | |
| 107 | +import { router as searchRouter } from './src/endpoints/search.js'; | |
| 108 | +import { router as openRouterRouter } from './src/endpoints/openrouter.js'; | |
| 109 | +import { router as chatCompletionsRouter } from './src/endpoints/backends/chat-completions.js'; | |
| 110 | +import { router as koboldRouter } from './src/endpoints/backends/kobold.js'; | |
| 111 | +import { router as textCompletionsRouter } from './src/endpoints/backends/text-completions.js'; | |
| 112 | +import { router as scaleAltRouter } from './src/endpoints/backends/scale-alt.js'; | |
| 113 | +import { router as speechRouter } from './src/endpoints/speech.js'; | |
| 114 | +import { router as azureRouter } from './src/endpoints/azure.js'; | |
| 51 | 115 | |
| 52 | 116 | // Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0. |
| 53 | 117 | // https://github.com/nodejs/node/issues/47822#issuecomment-1564708870 |
| @@ -166,8 +230,8 @@ const cliArguments = yargs(hideBin(process.argv)) | ||
| 166 | 230 | }).parseSync(); |
| 167 | 231 | |
| 168 | 232 | // change all relative paths |
| 169 | -console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment.`); | |
| 233 | +const serverDirectory = import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url)); | |
| 170 | -const serverDirectory = __dirname; | |
| 234 | +console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment. Server directory: ${serverDirectory}`); | |
| 171 | 235 | process.chdir(serverDirectory); |
| 172 | 236 | |
| 173 | 237 | const app = express(); |
| @@ -188,7 +252,7 @@ const basicAuthMode = cliArguments.basicAuthMode ?? getConfigValue('basicAuthMod | ||
| 188 | 252 | const perUserBasicAuth = getConfigValue('perUserBasicAuth', DEFAULT_PER_USER_BASIC_AUTH); |
| 189 | 253 | const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS); |
| 190 | 254 | |
| 191 | 255 | const uploadsPath = path.join(dataRoot, require('./src/constants').UPLOADS_DIRECTORY); |
| 192 | 256 | |
| 193 | 257 | const enableIPv6 = cliArguments.enableIPv6 ?? getConfigValue('protocol.ipv6', DEFAULT_ENABLE_IPV6); |
| 194 | 258 | const enableIPv4 = cliArguments.enableIPv4 ?? getConfigValue('protocol.ipv4', DEFAULT_ENABLE_IPV4); |
| @@ -232,7 +296,6 @@ if (listen && basicAuthMode) app.use(basicAuthMiddleware); | ||
| 232 | 296 | app.use(whitelistMiddleware(enableWhitelist, listen)); |
| 233 | 297 | |
| 234 | 298 | if (enableCorsProxy) { |
| 235 | - const bodyParser = require('body-parser'); | |
| 236 | 299 | app.use(bodyParser.json({ |
| 237 | 300 | limit: '200mb', |
| 238 | 301 | })); |
| @@ -301,24 +364,23 @@ function getSessionCookieAge() { | ||
| 301 | 364 | } |
| 302 | 365 | |
| 303 | 366 | app.use(cookieSession({ |
| 304 | 367 | name: userModule.getCookieSessionName(), |
| 305 | 368 | sameSite: 'strict', |
| 306 | 369 | httpOnly: true, |
| 307 | 370 | maxAge: getSessionCookieAge(), |
| 308 | 371 | secret: userModule.getCookieSecret(), |
| 309 | 372 | })); |
| 310 | 373 | |
| 311 | 374 | app.use(userModule.setUserDataMiddleware); |
| 312 | 375 | |
| 313 | 376 | // CSRF Protection // |
| 314 | 377 | if (!disableCsrf) { |
| 315 | 378 | const COOKIES_SECRET = userModule.getCookieSecret(); |
| 316 | 379 | |
| 317 | 380 | const { generateToken, doubleCsrfProtection } = doubleCsrf({ |
| 318 | 381 | getSecret: userModule.getCsrfSecret, |
| 319 | 382 | cookieName: 'X-CSRF-Token', |
| 320 | 383 | cookieOptions: { |
| 321 | - httpOnly: true, | |
| 322 | 384 | sameSite: 'strict', |
| 323 | 385 | secure: false, |
| 324 | 386 | }, |
| @@ -346,7 +408,7 @@ if (!disableCsrf) { | ||
| 346 | 408 | // Static files |
| 347 | 409 | // Host index page |
| 348 | 410 | app.get('/', (request, response) => { |
| 349 | 411 | if (userModule.shouldRedirectToLogin(request)) { |
| 350 | 412 | const query = request.url.split('?')[1]; |
| 351 | 413 | const redirectUrl = query ? `/login?${query}` : '/login'; |
| 352 | 414 | return response.redirect(redirectUrl); |
| @@ -363,7 +425,7 @@ app.get('/login', async (request, response) => { | ||
| 363 | 425 | } |
| 364 | 426 | |
| 365 | 427 | try { |
| 366 | 428 | const autoLogin = await userModule.tryAutoLogin(request, basicAuthMode); |
| 367 | 429 | |
| 368 | 430 | if (autoLogin) { |
| 369 | 431 | return response.redirect('/'); |
| @@ -379,22 +441,22 @@ app.get('/login', async (request, response) => { | ||
| 379 | 441 | app.use(express.static(process.cwd() + '/public', {})); |
| 380 | 442 | |
| 381 | 443 | // Public API |
| 382 | 444 | app.use('/api/users', require('./src/endpoints/users-public').routerusersPublicRouter); |
| 383 | 445 | |
| 384 | 446 | // Everything below this line requires authentication |
| 385 | 447 | app.use(userModule.requireLoginMiddleware); |
| 386 | 448 | app.get('/api/ping', (_, response) => response.sendStatus(204)); |
| 387 | 449 | |
| 388 | 450 | // File uploads |
| 389 | 451 | app.use(multer({ dest: uploadsPath, limits: { fieldSize: 10 * 1024 * 1024 } }).single('avatar')); |
| 390 | 452 | app.use(require('./src/middleware/multerMonkeyPatch')); |
| 391 | 453 | |
| 392 | 454 | // User data mount |
| 393 | 455 | app.use('/', userModule.routeruserDataRouter); |
| 394 | 456 | // Private endpoints |
| 395 | 457 | app.use('/api/users', require('./src/endpoints/users-private').routerusersPrivateRouter); |
| 396 | 458 | // Admin endpoints |
| 397 | 459 | app.use('/api/users', require('./src/endpoints/users-admin').routerusersAdminRouter); |
| 398 | 460 | |
| 399 | 461 | app.get('/version', async function (_, response) { |
| 400 | 462 | const data = await getVersion(); |
| @@ -509,126 +571,45 @@ redirect('/api/serpapi/search', '/api/search/serpapi'); | ||
| 509 | 571 | redirect('/api/serpapi/visit', '/api/search/visit'); |
| 510 | 572 | redirect('/api/serpapi/transcript', '/api/search/transcript'); |
| 511 | 573 | |
| 512 | -// Moving UI | |
| 574 | +app.use('/api/moving-ui', movingUIRouter); | |
| 513 | 575 | app.use('/api/moving-uiimages', require('./src/endpoints/moving-ui').routerimagesRouter); |
| 514 | - | |
| 576 | +app.use('/api/quick-replies', quickRepliesRouter); | |
| 515 | -// Image management | |
| 577 | +app.use('/api/avatars', avatarsRouter); | |
| 516 | 578 | app.use('/api/imagesthemes', require('./src/endpoints/images').routerthemesRouter); |
| 517 | - | |
| 579 | +app.use('/api/openai', openAiRouter); | |
| 518 | -// Quick reply management | |
| 580 | +app.use('/api/google', googleRouter); | |
| 519 | 581 | app.use('/api/quick-repliesanthropic', require('./src/endpoints/quick-replies').routeranthropicRouter); |
| 520 | - | |
| 582 | +app.use('/api/tokenizers', tokenizersRouter); | |
| 521 | -// Avatar management | |
| 583 | +app.use('/api/presets', presetsRouter); | |
| 522 | 584 | app.use('/api/avatarssecrets', require('./src/endpoints/avatars').routersecretsRouter); |
| 523 | - | |
| 585 | +app.use('/thumbnail', thumbnailRouter); | |
| 524 | -// Theme management | |
| 586 | +app.use('/api/novelai', novelAiRouter); | |
| 525 | 587 | app.use('/api/themesextensions', require('./src/endpoints/themes').routerextensionsRouter); |
| 526 | - | |
| 588 | +app.use('/api/assets', assetsRouter); | |
| 527 | -// OpenAI API | |
| 589 | +app.use('/api/files', filesRouter); | |
| 528 | 590 | app.use('/api/openaicharacters', require('./src/endpoints/openai').routercharactersRouter); |
| 529 | - | |
| 591 | +app.use('/api/chats', chatsRouter); | |
| 530 | -//Google API | |
| 592 | +app.use('/api/groups', groupsRouter); | |
| 531 | 593 | app.use('/api/googleworldinfo', require('./src/endpoints/google').routerworldInfoRouter); |
| 532 | - | |
| 594 | +app.use('/api/stats', statsRouter); | |
| 533 | -//Anthropic API | |
| 595 | +app.use('/api/backgrounds', backgroundsRouter); | |
| 534 | 596 | app.use('/api/anthropicsprites', require('./src/endpoints/anthropic').routerspritesRouter); |
| 535 | - | |
| 597 | +app.use('/api/content', contentManagerRouter); | |
| 536 | -// Tokenizers | |
| 598 | +app.use('/api/settings', settingsRouter); | |
| 537 | 599 | app.use('/api/tokenizerssd', require('./src/endpoints/tokenizers').routerstableDiffusionRouter); |
| 538 | - | |
| 600 | +app.use('/api/horde', hordeRouter); | |
| 539 | -// Preset management | |
| 601 | +app.use('/api/vector', vectorsRouter); | |
| 540 | 602 | app.use('/api/presetstranslate', require('./src/endpoints/presets').routertranslateRouter); |
| 541 | - | |
| 603 | +app.use('/api/extra/classify', classifyRouter); | |
| 542 | -// Secrets managemenet | |
| 604 | +app.use('/api/extra/caption', captionRouter); | |
| 543 | 605 | app.use('/api/secretssearch', require('./src/endpoints/secrets').routersearchRouter); |
| 544 | - | |
| 606 | +app.use('/api/backends/text-completions', textCompletionsRouter); | |
| 545 | -// Thumbnail generation. These URLs are saved in chat, so this route cannot be renamed! | |
| 607 | +app.use('/api/openrouter', openRouterRouter); | |
| 546 | 608 | app.use('/thumbnail', require('./srcapi/endpointsbackends/thumbnailskobold').router, koboldRouter); |
| 547 | - | |
| 609 | +app.use('/api/backends/chat-completions', chatCompletionsRouter); | |
| 548 | -// NovelAI generation | |
| 610 | +app.use('/api/backends/scale-alt', scaleAltRouter); | |
| 549 | 611 | app.use('/api/novelaispeech', require('./src/endpoints/novelai').routerspeechRouter); |
| 550 | - | |
| 612 | +app.use('/api/azure', azureRouter); | |
| 551 | -// Third-party extensions | |
| 552 | -app.use('/api/extensions', require('./src/endpoints/extensions').router); | |
| 553 | - | |
| 554 | -// Asset management | |
| 555 | -app.use('/api/assets', require('./src/endpoints/assets').router); | |
| 556 | - | |
| 557 | -// File management | |
| 558 | -app.use('/api/files', require('./src/endpoints/files').router); | |
| 559 | - | |
| 560 | -// Character management | |
| 561 | -app.use('/api/characters', require('./src/endpoints/characters').router); | |
| 562 | - | |
| 563 | -// Chat management | |
| 564 | -app.use('/api/chats', require('./src/endpoints/chats').router); | |
| 565 | - | |
| 566 | -// Group management | |
| 567 | -app.use('/api/groups', require('./src/endpoints/groups').router); | |
| 568 | - | |
| 569 | -// World info management | |
| 570 | -app.use('/api/worldinfo', require('./src/endpoints/worldinfo').router); | |
| 571 | - | |
| 572 | -// Stats calculation | |
| 573 | -const statsEndpoint = require('./src/endpoints/stats'); | |
| 574 | -app.use('/api/stats', statsEndpoint.router); | |
| 575 | - | |
| 576 | -// Background management | |
| 577 | -app.use('/api/backgrounds', require('./src/endpoints/backgrounds').router); | |
| 578 | - | |
| 579 | -// Character sprite management | |
| 580 | -app.use('/api/sprites', require('./src/endpoints/sprites').router); | |
| 581 | - | |
| 582 | -// Custom content management | |
| 583 | -app.use('/api/content', require('./src/endpoints/content-manager').router); | |
| 584 | - | |
| 585 | -// Settings load/store | |
| 586 | -const settingsEndpoint = require('./src/endpoints/settings'); | |
| 587 | -app.use('/api/settings', settingsEndpoint.router); | |
| 588 | - | |
| 589 | -// Stable Diffusion generation | |
| 590 | -app.use('/api/sd', require('./src/endpoints/stable-diffusion').router); | |
| 591 | - | |
| 592 | -// LLM and SD Horde generation | |
| 593 | -app.use('/api/horde', require('./src/endpoints/horde').router); | |
| 594 | - | |
| 595 | -// Vector storage DB | |
| 596 | -app.use('/api/vector', require('./src/endpoints/vectors').router); | |
| 597 | - | |
| 598 | -// Chat translation | |
| 599 | -app.use('/api/translate', require('./src/endpoints/translate').router); | |
| 600 | - | |
| 601 | -// Emotion classification | |
| 602 | -app.use('/api/extra/classify', require('./src/endpoints/classify').router); | |
| 603 | - | |
| 604 | -// Image captioning | |
| 605 | -app.use('/api/extra/caption', require('./src/endpoints/caption').router); | |
| 606 | - | |
| 607 | -// Web search and scraping | |
| 608 | -app.use('/api/search', require('./src/endpoints/search').router); | |
| 609 | - | |
| 610 | -// The different text generation APIs | |
| 611 | - | |
| 612 | -// Ooba/OpenAI text completions | |
| 613 | -app.use('/api/backends/text-completions', require('./src/endpoints/backends/text-completions').router); | |
| 614 | - | |
| 615 | -// OpenRouter | |
| 616 | -app.use('/api/openrouter', require('./src/endpoints/openrouter').router); | |
| 617 | - | |
| 618 | -// KoboldAI | |
| 619 | -app.use('/api/backends/kobold', require('./src/endpoints/backends/kobold').router); | |
| 620 | - | |
| 621 | -// OpenAI chat completions | |
| 622 | -app.use('/api/backends/chat-completions', require('./src/endpoints/backends/chat-completions').router); | |
| 623 | - | |
| 624 | -// Scale (alt method) | |
| 625 | -app.use('/api/backends/scale-alt', require('./src/endpoints/backends/scale-alt').router); | |
| 626 | - | |
| 627 | -// Speech (text-to-speech and speech-to-text) | |
| 628 | -app.use('/api/speech', require('./src/endpoints/speech').router); | |
| 629 | - | |
| 630 | -// Azure TTS | |
| 631 | -app.use('/api/azure', require('./src/endpoints/azure').router); | |
| 632 | 613 | |
| 633 | 614 | const tavernUrlV6 = new URL( |
| 634 | 615 | (cliArguments.ssl ? 'https://' : 'http://') + |
| @@ -658,22 +639,22 @@ const preSetupTasks = async function () { | ||
| 658 | 639 | } |
| 659 | 640 | console.log(); |
| 660 | 641 | |
| 661 | 642 | const directories = await userModule.getUserDirectoriesList(); |
| 662 | 643 | await contentManager.checkForNewContent(directories); |
| 663 | 644 | await ensureThumbnailCache(); |
| 664 | 645 | cleanUploads(); |
| 665 | 646 | |
| 666 | 647 | await settingsEndpoint.initsettingsInit(); |
| 667 | 648 | await statsEndpoint.initstatsInit(); |
| 668 | 649 | |
| 669 | 650 | const cleanupPlugins = await loadPluginsinitializePlugins(); |
| 670 | 651 | const consoleTitle = process.title; |
| 671 | 652 | |
| 672 | 653 | let isExiting = false; |
| 673 | 654 | const exitProcess = async () => { |
| 674 | 655 | if (isExiting) return; |
| 675 | 656 | isExiting = true; |
| 676 | 657 | statsEndpoint.onExitawait statsOnExit(); |
| 677 | 658 | if (typeof cleanupPlugins === 'function') { |
| 678 | 659 | await cleanupPlugins(); |
| 679 | 660 | } |
| @@ -773,11 +754,10 @@ const postSetupTasks = async function (v6Failed, v4Failed) { | ||
| 773 | 754 | * Loads server plugins from a directory. |
| 774 | 755 | * @returns {Promise<Function>} Function to be run on server exit |
| 775 | 756 | */ |
| 776 | 757 | async function loadPluginsinitializePlugins() { |
| 777 | 758 | try { |
| 778 | 759 | const pluginDirectory = path.join(serverDirectory, 'plugins'); |
| 779 | - const loader = require('./src/plugin-loader'); | |
| 760 | + const cleanupPlugins = await loadPlugins(app, pluginDirectory); | |
| 780 | - const cleanupPlugins = await loader.loadPlugins(app, pluginDirectory); | |
| 781 | 761 | return cleanupPlugins; |
| 782 | 762 | } catch { |
| 783 | 763 | console.log('Plugin loading failed.'); |
| @@ -850,7 +830,7 @@ function createHttpsServer(url) { | ||
| 850 | 830 | }, app); |
| 851 | 831 | server.on('error', reject); |
| 852 | 832 | server.on('listening', resolve); |
| 853 | 833 | server.listen(Number(url.port || 443), url.hostname); |
| 854 | 834 | }); |
| 855 | 835 | } |
| 856 | 836 | |
| @@ -865,7 +845,7 @@ function createHttpServer(url) { | ||
| 865 | 845 | const server = http.createServer(app); |
| 866 | 846 | server.on('error', reject); |
| 867 | 847 | server.on('listening', resolve); |
| 868 | 848 | server.listen(Number(url.port || 80), url.hostname); |
| 869 | 849 | }); |
| 870 | 850 | } |
| 871 | 851 | |
| @@ -917,7 +897,7 @@ async function verifySecuritySettings() { | ||
| 917 | 897 | logSecurityAlert('Your SillyTavern is currently insecurely open to the public. Enable whitelisting, basic authentication or user accounts.'); |
| 918 | 898 | } |
| 919 | 899 | |
| 920 | 900 | const users = await userModule.getAllEnabledUsers(); |
| 921 | 901 | const unprotectedUsers = users.filter(x => !x.password); |
| 922 | 902 | const unprotectedAdminUsers = unprotectedUsers.filter(x => x.admin); |
| 923 | 903 | |
| @@ -935,10 +915,10 @@ async function verifySecuritySettings() { | ||
| 935 | 915 | } |
| 936 | 916 | |
| 937 | 917 | // User storage module needs to be initialized before starting the server |
| 938 | 918 | userModule.initUserStorage(dataRoot) |
| 939 | 919 | .then(userModule.ensurePublicDirectoriesExist) |
| 940 | 920 | .then(userModule.migrateUserData) |
| 941 | 921 | .then(userModule.migrateSystemPrompts) |
| 942 | 922 | .then(verifySecuritySettings) |
| 943 | 923 | .then(preSetupTasks) |
| 944 | 924 | .finally(startServer); |
| @@ -1,10 +1,10 @@ | ||
| 1 | 1 | constimport { TEXTGEN_TYPES, OPENROUTER_HEADERS, FEATHERLESS_HEADERS } =from require('./constants.js'); |
| 2 | 2 | constimport { SECRET_KEYS, readSecret } =from require('./endpoints/secrets.js'); |
| 3 | 3 | constimport { getConfigValue } =from require('./util.js'); |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * Gets the headers for the Mancer API. |
| 7 | 7 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 8 | 8 | * @returns {object} Headers for the request |
| 9 | 9 | */ |
| 10 | 10 | function getMancerHeaders(directories) { |
| @@ -18,7 +18,7 @@ function getMancerHeaders(directories) { | ||
| 18 | 18 | |
| 19 | 19 | /** |
| 20 | 20 | * Gets the headers for the TogetherAI API. |
| 21 | 21 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 22 | 22 | * @returns {object} Headers for the request |
| 23 | 23 | */ |
| 24 | 24 | function getTogetherAIHeaders(directories) { |
| @@ -31,7 +31,7 @@ function getTogetherAIHeaders(directories) { | ||
| 31 | 31 | |
| 32 | 32 | /** |
| 33 | 33 | * Gets the headers for the InfermaticAI API. |
| 34 | 34 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 35 | 35 | * @returns {object} Headers for the request |
| 36 | 36 | */ |
| 37 | 37 | function getInfermaticAIHeaders(directories) { |
| @@ -44,7 +44,7 @@ function getInfermaticAIHeaders(directories) { | ||
| 44 | 44 | |
| 45 | 45 | /** |
| 46 | 46 | * Gets the headers for the DreamGen API. |
| 47 | 47 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 48 | 48 | * @returns {object} Headers for the request |
| 49 | 49 | */ |
| 50 | 50 | function getDreamGenHeaders(directories) { |
| @@ -57,7 +57,7 @@ function getDreamGenHeaders(directories) { | ||
| 57 | 57 | |
| 58 | 58 | /** |
| 59 | 59 | * Gets the headers for the OpenRouter API. |
| 60 | 60 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 61 | 61 | * @returns {object} Headers for the request |
| 62 | 62 | */ |
| 63 | 63 | function getOpenRouterHeaders(directories) { |
| @@ -69,7 +69,7 @@ function getOpenRouterHeaders(directories) { | ||
| 69 | 69 | |
| 70 | 70 | /** |
| 71 | 71 | * Gets the headers for the vLLM API. |
| 72 | 72 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 73 | 73 | * @returns {object} Headers for the request |
| 74 | 74 | */ |
| 75 | 75 | function getVllmHeaders(directories) { |
| @@ -82,7 +82,7 @@ function getVllmHeaders(directories) { | ||
| 82 | 82 | |
| 83 | 83 | /** |
| 84 | 84 | * Gets the headers for the Aphrodite API. |
| 85 | 85 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 86 | 86 | * @returns {object} Headers for the request |
| 87 | 87 | */ |
| 88 | 88 | function getAphroditeHeaders(directories) { |
| @@ -96,7 +96,7 @@ function getAphroditeHeaders(directories) { | ||
| 96 | 96 | |
| 97 | 97 | /** |
| 98 | 98 | * Gets the headers for the Tabby API. |
| 99 | 99 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 100 | 100 | * @returns {object} Headers for the request |
| 101 | 101 | */ |
| 102 | 102 | function getTabbyHeaders(directories) { |
| @@ -110,7 +110,7 @@ function getTabbyHeaders(directories) { | ||
| 110 | 110 | |
| 111 | 111 | /** |
| 112 | 112 | * Gets the headers for the LlamaCPP API. |
| 113 | 113 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 114 | 114 | * @returns {object} Headers for the request |
| 115 | 115 | */ |
| 116 | 116 | function getLlamaCppHeaders(directories) { |
| @@ -123,7 +123,7 @@ function getLlamaCppHeaders(directories) { | ||
| 123 | 123 | |
| 124 | 124 | /** |
| 125 | 125 | * Gets the headers for the Ooba API. |
| 126 | 126 | * @param {import('./users.js').UserDirectoryList} directories |
| 127 | 127 | * @returns {object} Headers for the request |
| 128 | 128 | */ |
| 129 | 129 | function getOobaHeaders(directories) { |
| @@ -136,7 +136,7 @@ function getOobaHeaders(directories) { | ||
| 136 | 136 | |
| 137 | 137 | /** |
| 138 | 138 | * Gets the headers for the KoboldCpp API. |
| 139 | 139 | * @param {import('./users.js').UserDirectoryList} directories |
| 140 | 140 | * @returns {object} Headers for the request |
| 141 | 141 | */ |
| 142 | 142 | function getKoboldCppHeaders(directories) { |
| @@ -149,7 +149,7 @@ function getKoboldCppHeaders(directories) { | ||
| 149 | 149 | |
| 150 | 150 | /** |
| 151 | 151 | * Gets the headers for the Featherless API. |
| 152 | 152 | * @param {import('./users.js').UserDirectoryList} directories |
| 153 | 153 | * @returns {object} Headers for the request |
| 154 | 154 | */ |
| 155 | 155 | function getFeatherlessHeaders(directories) { |
| @@ -161,7 +161,7 @@ function getFeatherlessHeaders(directories) { | ||
| 161 | 161 | |
| 162 | 162 | /** |
| 163 | 163 | * Gets the headers for the HuggingFace API. |
| 164 | 164 | * @param {import('./users.js').UserDirectoryList} directories |
| 165 | 165 | * @returns {object} Headers for the request |
| 166 | 166 | */ |
| 167 | 167 | function getHuggingFaceHeaders(directories) { |
| @@ -172,7 +172,7 @@ function getHuggingFaceHeaders(directories) { | ||
| 172 | 172 | }) : {}; |
| 173 | 173 | } |
| 174 | 174 | |
| 175 | 175 | export function getOverrideHeaders(urlHost) { |
| 176 | 176 | const requestOverrides = getConfigValue('requestOverrides', []); |
| 177 | 177 | const overrideHeaders = requestOverrides?.find((e) => e.hosts?.includes(urlHost))?.headers; |
| 178 | 178 | if (overrideHeaders && urlHost) { |
| @@ -188,7 +188,7 @@ function getOverrideHeaders(urlHost) { | ||
| 188 | 188 | * @param {object} args New request arguments |
| 189 | 189 | * @param {string|null} server API server for new request |
| 190 | 190 | */ |
| 191 | 191 | export function setAdditionalHeaders(request, args, server) { |
| 192 | 192 | setAdditionalHeadersByType(args.headers, request.body.api_type, server, request.user.directories); |
| 193 | 193 | } |
| 194 | 194 | |
| @@ -197,9 +197,9 @@ function setAdditionalHeaders(request, args, server) { | ||
| 197 | 197 | * @param {object} requestHeaders Request headers |
| 198 | 198 | * @param {string} type API type |
| 199 | 199 | * @param {string|null} server API server for new request |
| 200 | 200 | * @param {import('./users.js').UserDirectoryList} directories User directories |
| 201 | 201 | */ |
| 202 | 202 | export function setAdditionalHeadersByType(requestHeaders, type, server, directories) { |
| 203 | 203 | const headerGetters = { |
| 204 | 204 | [TEXTGEN_TYPES.MANCER]: getMancerHeaders, |
| 205 | 205 | [TEXTGEN_TYPES.VLLM]: getVllmHeaders, |
| @@ -234,9 +234,3 @@ function setAdditionalHeadersByType(requestHeaders, type, server, directories) { | ||
| 234 | 234 | |
| 235 | 235 | Object.assign(requestHeaders, headers); |
| 236 | 236 | } |
| 237 | - | |
| 238 | -module.exports = { | |
| 239 | - getOverrideHeaders, | |
| 240 | - setAdditionalHeaders, | |
| 241 | - setAdditionalHeadersByType, | |
| 242 | -}; | |
| @@ -1,8 +1,9 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | +import { Buffer } from 'node:buffer'; | |
| 2 | 3 | |
| 3 | 4 | constimport encode =from require('png-chunks-encode'); |
| 4 | 5 | constimport extract =from require('png-chunks-extract'); |
| 5 | 6 | constimport PNGtext =from require('png-chunk-text'); |
| 6 | 7 | |
| 7 | 8 | /** |
| 8 | 9 | * Writes Character metadata to a PNG image buffer. |
| @@ -11,8 +12,8 @@ const PNGtext = require('png-chunk-text'); | ||
| 11 | 12 | * @param {string} data Character data to write |
| 12 | 13 | * @returns {Buffer} PNG image buffer with metadata |
| 13 | 14 | */ |
| 14 | 15 | export const write = (image, data) => { |
| 15 | 16 | const chunks = extract(new Uint8Array(image)); |
| 16 | 17 | const tEXtChunks = chunks.filter(chunk => chunk.name === 'tEXt'); |
| 17 | 18 | |
| 18 | 19 | // Remove existing tEXt chunks |
| @@ -36,7 +37,9 @@ const write = (image, data) => { | ||
| 36 | 37 | |
| 37 | 38 | const base64EncodedData = Buffer.from(JSON.stringify(v3Data), 'utf8').toString('base64'); |
| 38 | 39 | chunks.splice(-1, 0, PNGtext.encode('ccv3', base64EncodedData)); |
| 39 | 40 | } catch (error) { } |
| 41 | + // Ignore errors when adding v3 chunk | |
| 42 | + } | |
| 40 | 43 | |
| 41 | 44 | const newBuffer = Buffer.from(encode(chunks)); |
| 42 | 45 | return newBuffer; |
| @@ -48,8 +51,8 @@ const write = (image, data) => { | ||
| 48 | 51 | * @param {Buffer} image PNG image buffer |
| 49 | 52 | * @returns {string} Character data |
| 50 | 53 | */ |
| 51 | 54 | export const read = (image) => { |
| 52 | 55 | const chunks = extract(new Uint8Array(image)); |
| 53 | 56 | |
| 54 | 57 | const textChunks = chunks.filter((chunk) => chunk.name === 'tEXt').map((chunk) => PNGtext.decode(chunk.data)); |
| 55 | 58 | |
| @@ -80,7 +83,7 @@ const read = (image) => { | ||
| 80 | 83 | * @param {string} format File format |
| 81 | 84 | * @returns {string} Character data |
| 82 | 85 | */ |
| 83 | 86 | export const parse = (cardUrl, format) => { |
| 84 | 87 | let fileFormat = format === undefined ? 'png' : format; |
| 85 | 88 | |
| 86 | 89 | switch (fileFormat) { |
| @@ -93,8 +96,3 @@ const parse = (cardUrl, format) => { | ||
| 93 | 96 | throw new Error('Unsupported format'); |
| 94 | 97 | }; |
| 95 | 98 | |
| 96 | -module.exports = { | |
| 97 | - parse, | |
| 98 | - write, | |
| 99 | - read, | |
| 100 | -}; | |
| @@ -1,18 +1,18 @@ | ||
| 1 | 1 | export const PUBLIC_DIRECTORIES = { |
| 2 | 2 | images: 'public/img/', |
| 3 | 3 | backups: 'backups/', |
| 4 | 4 | sounds: 'public/sounds', |
| 5 | 5 | extensions: 'public/scripts/extensions', |
| 6 | 6 | }; |
| 7 | 7 | |
| 8 | 8 | export const SETTINGS_FILE = 'settings.json'; |
| 9 | 9 | |
| 10 | 10 | /** |
| 11 | 11 | * @type {import('./users.js').UserDirectoryList} |
| 12 | 12 | * @readonly |
| 13 | 13 | * @enum {string} |
| 14 | 14 | */ |
| 15 | 15 | export const USER_DIRECTORY_TEMPLATE = Object.freeze({ |
| 16 | 16 | root: '', |
| 17 | 17 | thumbnails: 'thumbnails', |
| 18 | 18 | thumbnailsBg: 'thumbnails/bg', |
| @@ -45,10 +45,10 @@ const USER_DIRECTORY_TEMPLATE = Object.freeze({ | ||
| 45 | 45 | }); |
| 46 | 46 | |
| 47 | 47 | /** |
| 48 | 48 | * @type {import('./users.js').User} |
| 49 | 49 | * @readonly |
| 50 | 50 | */ |
| 51 | 51 | export const DEFAULT_USER = Object.freeze({ |
| 52 | 52 | handle: 'default-user', |
| 53 | 53 | name: 'User', |
| 54 | 54 | created: Date.now(), |
| @@ -58,7 +58,7 @@ const DEFAULT_USER = Object.freeze({ | ||
| 58 | 58 | salt: '', |
| 59 | 59 | }); |
| 60 | 60 | |
| 61 | 61 | export const UNSAFE_EXTENSIONS = [ |
| 62 | 62 | '.php', |
| 63 | 63 | '.exe', |
| 64 | 64 | '.com', |
| @@ -135,7 +135,7 @@ const UNSAFE_EXTENSIONS = [ | ||
| 135 | 135 | '.ws', |
| 136 | 136 | ]; |
| 137 | 137 | |
| 138 | 138 | export const GEMINI_SAFETY = [ |
| 139 | 139 | { |
| 140 | 140 | category: 'HARM_CATEGORY_HARASSMENT', |
| 141 | 141 | threshold: 'BLOCK_NONE', |
| @@ -158,7 +158,7 @@ const GEMINI_SAFETY = [ | ||
| 158 | 158 | }, |
| 159 | 159 | ]; |
| 160 | 160 | |
| 161 | 161 | export const BISON_SAFETY = [ |
| 162 | 162 | { |
| 163 | 163 | category: 'HARM_CATEGORY_DEROGATORY', |
| 164 | 164 | threshold: 'BLOCK_NONE', |
| @@ -185,7 +185,7 @@ const BISON_SAFETY = [ | ||
| 185 | 185 | }, |
| 186 | 186 | ]; |
| 187 | 187 | |
| 188 | 188 | export const CHAT_COMPLETION_SOURCES = { |
| 189 | 189 | OPENAI: 'openai', |
| 190 | 190 | WINDOWAI: 'windowai', |
| 191 | 191 | CLAUDE: 'claude', |
| @@ -205,10 +205,10 @@ const CHAT_COMPLETION_SOURCES = { | ||
| 205 | 205 | /** |
| 206 | 206 | * Path to multer file uploads under the data root. |
| 207 | 207 | */ |
| 208 | 208 | export const UPLOADS_DIRECTORY = '_uploads'; |
| 209 | 209 | |
| 210 | 210 | // TODO: this is copied from the client code; there should be a way to de-duplicate it eventually |
| 211 | 211 | export const TEXTGEN_TYPES = { |
| 212 | 212 | OOBA: 'ooba', |
| 213 | 213 | MANCER: 'mancer', |
| 214 | 214 | VLLM: 'vllm', |
| @@ -225,7 +225,7 @@ const TEXTGEN_TYPES = { | ||
| 225 | 225 | HUGGINGFACE: 'huggingface', |
| 226 | 226 | }; |
| 227 | 227 | |
| 228 | 228 | export const INFERMATICAI_KEYS = [ |
| 229 | 229 | 'model', |
| 230 | 230 | 'prompt', |
| 231 | 231 | 'max_tokens', |
| @@ -248,7 +248,7 @@ const INFERMATICAI_KEYS = [ | ||
| 248 | 248 | 'logprobs', |
| 249 | 249 | ]; |
| 250 | 250 | |
| 251 | 251 | export const FEATHERLESS_KEYS = [ |
| 252 | 252 | 'model', |
| 253 | 253 | 'prompt', |
| 254 | 254 | 'best_of', |
| @@ -290,9 +290,8 @@ const FEATHERLESS_KEYS = [ | ||
| 290 | 290 | 'guided_whitespace_pattern', |
| 291 | 291 | ]; |
| 292 | 292 | |
| 293 | - | |
| 294 | 293 | // https://dreamgen.com/docs/api#openai-text |
| 295 | 294 | export const DREAMGEN_KEYS = [ |
| 296 | 295 | 'model', |
| 297 | 296 | 'prompt', |
| 298 | 297 | 'max_tokens', |
| @@ -309,7 +308,7 @@ const DREAMGEN_KEYS = [ | ||
| 309 | 308 | ]; |
| 310 | 309 | |
| 311 | 310 | // https://docs.together.ai/reference/completions |
| 312 | 311 | export const TOGETHERAI_KEYS = [ |
| 313 | 312 | 'model', |
| 314 | 313 | 'prompt', |
| 315 | 314 | 'max_tokens', |
| @@ -325,7 +324,7 @@ const TOGETHERAI_KEYS = [ | ||
| 325 | 324 | ]; |
| 326 | 325 | |
| 327 | 326 | // https://github.com/jmorganca/ollama/blob/main/docs/api.md#request-with-options |
| 328 | 327 | export const OLLAMA_KEYS = [ |
| 329 | 328 | 'num_predict', |
| 330 | 329 | 'num_ctx', |
| 331 | 330 | 'stop', |
| @@ -345,20 +344,20 @@ const OLLAMA_KEYS = [ | ||
| 345 | 344 | 'min_p', |
| 346 | 345 | ]; |
| 347 | 346 | |
| 348 | 347 | export const AVATAR_WIDTH = 512; |
| 349 | 348 | export const AVATAR_HEIGHT = 768; |
| 350 | 349 | |
| 351 | 350 | export const OPENROUTER_HEADERS = { |
| 352 | 351 | 'HTTP-Referer': 'https://sillytavern.app', |
| 353 | 352 | 'X-Title': 'SillyTavern', |
| 354 | 353 | }; |
| 355 | 354 | |
| 356 | 355 | export const FEATHERLESS_HEADERS = { |
| 357 | 356 | 'HTTP-Referer': 'https://sillytavern.app', |
| 358 | 357 | 'X-Title': 'SillyTavern', |
| 359 | 358 | }; |
| 360 | 359 | |
| 361 | 360 | export const OPENROUTER_KEYS = [ |
| 362 | 361 | 'max_tokens', |
| 363 | 362 | 'temperature', |
| 364 | 363 | 'top_k', |
| @@ -378,7 +377,7 @@ const OPENROUTER_KEYS = [ | ||
| 378 | 377 | ]; |
| 379 | 378 | |
| 380 | 379 | // https://github.com/vllm-project/vllm/blob/0f8a91401c89ac0a8018def3756829611b57727f/vllm/entrypoints/openai/protocol.py#L220 |
| 381 | 380 | export const VLLM_KEYS = [ |
| 382 | 381 | 'model', |
| 383 | 382 | 'prompt', |
| 384 | 383 | 'best_of', |
| @@ -419,27 +418,3 @@ const VLLM_KEYS = [ | ||
| 419 | 418 | 'guided_decoding_backend', |
| 420 | 419 | 'guided_whitespace_pattern', |
| 421 | 420 | ]; |
| 422 | - | |
| 423 | -module.exports = { | |
| 424 | - DEFAULT_USER, | |
| 425 | - SETTINGS_FILE, | |
| 426 | - PUBLIC_DIRECTORIES, | |
| 427 | - USER_DIRECTORY_TEMPLATE, | |
| 428 | - UNSAFE_EXTENSIONS, | |
| 429 | - UPLOADS_DIRECTORY, | |
| 430 | - GEMINI_SAFETY, | |
| 431 | - BISON_SAFETY, | |
| 432 | - TEXTGEN_TYPES, | |
| 433 | - CHAT_COMPLETION_SOURCES, | |
| 434 | - AVATAR_WIDTH, | |
| 435 | - AVATAR_HEIGHT, | |
| 436 | - TOGETHERAI_KEYS, | |
| 437 | - OLLAMA_KEYS, | |
| 438 | - INFERMATICAI_KEYS, | |
| 439 | - DREAMGEN_KEYS, | |
| 440 | - OPENROUTER_HEADERS, | |
| 441 | - OPENROUTER_KEYS, | |
| 442 | - VLLM_KEYS, | |
| 443 | - FEATHERLESS_KEYS, | |
| 444 | - FEATHERLESS_HEADERS, | |
| 445 | -}; | |
| @@ -1,9 +1,10 @@ | ||
| 1 | -const { readSecret, SECRET_KEYS } = require('./secrets'); | |
| 1 | +import fetch from 'node-fetch'; | |
| 2 | -const fetch = require('node-fetch').default; | |
| 2 | +import express from 'express'; | |
| 3 | -const express = require('express'); | |
| 4 | -const { jsonParser } = require('../express-common'); | |
| 5 | 3 | |
| 6 | -const router = express.Router(); | |
| 4 | +import { readSecret, SECRET_KEYS } from './secrets.js'; | |
| 5 | +import { jsonParser } from '../express-common.js'; | |
| 6 | + | |
| 7 | +export const router = express.Router(); | |
| 7 | 8 | |
| 8 | 9 | router.post('/caption-image', jsonParser, async (request, response) => { |
| 9 | 10 | try { |
| @@ -41,7 +42,6 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 41 | 42 | 'anthropic-version': '2023-06-01', |
| 42 | 43 | 'x-api-key': request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE), |
| 43 | 44 | }, |
| 44 | - timeout: 0, | |
| 45 | 45 | }); |
| 46 | 46 | |
| 47 | 47 | if (!result.ok) { |
| @@ -50,6 +50,7 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 50 | 50 | return response.status(result.status).send({ error: true }); |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | + /** @type {any} */ | |
| 53 | 54 | const generateResponseJson = await result.json(); |
| 54 | 55 | const caption = generateResponseJson.content[0].text; |
| 55 | 56 | console.log('Claude response:', generateResponseJson); |
| @@ -64,5 +65,3 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 64 | 65 | response.status(500).send('Internal server error'); |
| 65 | 66 | } |
| 66 | 67 | }); |
| 67 | - | |
| 68 | -module.exports = { router }; | |
| @@ -1,13 +1,15 @@ | ||
| 1 | 1 | constimport path =from require('node:path'); |
| 2 | 2 | constimport fs =from require('node:fs'); |
| 3 | -const mime = require('mime-types'); | |
| 3 | +import { finished } from 'node:stream/promises'; | |
| 4 | -const express = require('express'); | |
| 4 | + | |
| 5 | 5 | constimport sanitizemime =from require('sanitizemime-filenametypes'); |
| 6 | -const fetch = require('node-fetch').default; | |
| 6 | +import express from 'express'; | |
| 7 | -const { finished } = require('stream/promises'); | |
| 7 | +import sanitize from 'sanitize-filename'; | |
| 8 | -const { UNSAFE_EXTENSIONS } = require('../constants'); | |
| 8 | +import fetch from 'node-fetch'; | |
| 9 | -const { jsonParser } = require('../express-common'); | |
| 9 | + | |
| 10 | 10 | constimport { clientRelativePathUNSAFE_EXTENSIONS } =from require('../utilconstants.js'); |
| 11 | +import { jsonParser } from '../express-common.js'; | |
| 12 | +import { clientRelativePath } from '../util.js'; | |
| 11 | 13 | |
| 12 | 14 | const VALID_CATEGORIES = ['bgm', 'ambient', 'blip', 'live2d', 'vrm', 'character', 'temp']; |
| 13 | 15 | |
| @@ -16,7 +18,7 @@ const VALID_CATEGORIES = ['bgm', 'ambient', 'blip', 'live2d', 'vrm', 'character' | ||
| 16 | 18 | * @param {string} inputFilename Input filename |
| 17 | 19 | * @returns {{error: boolean, message?: string}} Whether validation failed, and why if so |
| 18 | 20 | */ |
| 19 | 21 | export function validateAssetFileName(inputFilename) { |
| 20 | 22 | if (!/^[a-zA-Z0-9_\-.]+$/.test(inputFilename)) { |
| 21 | 23 | return { |
| 22 | 24 | error: true, |
| @@ -77,7 +79,7 @@ function getFiles(dir, files = []) { | ||
| 77 | 79 | |
| 78 | 80 | /** |
| 79 | 81 | * Ensure that the asset folders exist. |
| 80 | 82 | * @param {import('../users.js').UserDirectoryList} directories - The user's directories |
| 81 | 83 | */ |
| 82 | 84 | function ensureFoldersExist(directories) { |
| 83 | 85 | const folderPath = path.join(directories.assets); |
| @@ -93,7 +95,7 @@ function ensureFoldersExist(directories) { | ||
| 93 | 95 | } |
| 94 | 96 | } |
| 95 | 97 | |
| 96 | 98 | export const router = express.Router(); |
| 97 | 99 | |
| 98 | 100 | /** |
| 99 | 101 | * HTTP POST handler function to retrieve name of all files of a given folder path. |
| @@ -366,5 +368,3 @@ router.post('/character', jsonParser, async (request, response) => { | ||
| 366 | 368 | return response.sendStatus(500); |
| 367 | 369 | } |
| 368 | 370 | }); |
| 369 | - | |
| 370 | -module.exports = { router, validateAssetFileName }; | |
| @@ -1,16 +1,16 @@ | ||
| 1 | -const express = require('express'); | |
| 1 | +import path from 'node:path'; | |
| 2 | -const path = require('path'); | |
| 2 | +import fs from 'node:fs'; | |
| 3 | -const fs = require('fs'); | |
| 4 | -const sanitize = require('sanitize-filename'); | |
| 5 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 6 | -const { jsonParser, urlencodedParser } = require('../express-common'); | |
| 7 | -const { AVATAR_WIDTH, AVATAR_HEIGHT } = require('../constants'); | |
| 8 | -const { getImages, tryParse } = require('../util'); | |
| 9 | 3 | |
| 10 | -// image processing related library imports | |
| 4 | +import express from 'express'; | |
| 11 | -const jimp = require('jimp'); | |
| 5 | +import sanitize from 'sanitize-filename'; | |
| 6 | +import jimp from 'jimp'; | |
| 7 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 12 | 8 | |
| 13 | -const router = express.Router(); | |
| 9 | +import { jsonParser, urlencodedParser } from '../express-common.js'; | |
| 10 | +import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js'; | |
| 11 | +import { getImages, tryParse } from '../util.js'; | |
| 12 | + | |
| 13 | +export const router = express.Router(); | |
| 14 | 14 | |
| 15 | 15 | router.post('/get', jsonParser, function (request, response) { |
| 16 | 16 | var images = getImages(request.user.directories.avatars); |
| @@ -58,5 +58,3 @@ router.post('/upload', urlencodedParser, async (request, response) => { | ||
| 58 | 58 | return response.status(400).send('Is not a valid image'); |
| 59 | 59 | } |
| 60 | 60 | }); |
| 61 | - | |
| 62 | -module.exports = { router }; | |
| @@ -1,9 +1,10 @@ | ||
| 1 | -const { readSecret, SECRET_KEYS } = require('./secrets'); | |
| 1 | +import fetch from 'node-fetch'; | |
| 2 | -const fetch = require('node-fetch').default; | |
| 2 | +import { Router } from 'express'; | |
| 3 | -const express = require('express'); | |
| 4 | -const { jsonParser } = require('../express-common'); | |
| 5 | 3 | |
| 6 | -const router = express.Router(); | |
| 4 | +import { readSecret, SECRET_KEYS } from './secrets.js'; | |
| 5 | +import { jsonParser } from '../express-common.js'; | |
| 6 | + | |
| 7 | +export const router = Router(); | |
| 7 | 8 | |
| 8 | 9 | router.post('/list', jsonParser, async (req, res) => { |
| 9 | 10 | try { |
| @@ -86,7 +87,3 @@ router.post('/generate', jsonParser, async (req, res) => { | ||
| 86 | 87 | return res.sendStatus(500); |
| 87 | 88 | } |
| 88 | 89 | }); |
| 89 | - | |
| 90 | -module.exports = { | |
| 91 | - router, | |
| 92 | -}; | |
| @@ -1,13 +1,41 @@ | ||
| 1 | -const express = require('express'); | |
| 1 | +import process from 'node:process'; | |
| 2 | -const fetch = require('node-fetch').default; | |
| 2 | +import express from 'express'; | |
| 3 | - | |
| 3 | +import fetch from 'node-fetch'; | |
| 4 | -const { jsonParser } = require('../../express-common'); | |
| 4 | + | |
| 5 | 5 | constimport { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERSjsonParser } =from require('../../constantsexpress-common.js'); |
| 6 | -const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util'); | |
| 6 | +import { | |
| 7 | -const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertAI21Messages, mergeMessages } = require('../../prompt-converters'); | |
| 7 | + CHAT_COMPLETION_SOURCES, | |
| 8 | - | |
| 8 | + GEMINI_SAFETY, | |
| 9 | -const { readSecret, SECRET_KEYS } = require('../secrets'); | |
| 9 | + BISON_SAFETY, | |
| 10 | -const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers'); | |
| 10 | + OPENROUTER_HEADERS, | |
| 11 | +} from '../../constants.js'; | |
| 12 | +import { | |
| 13 | + forwardFetchResponse, | |
| 14 | + getConfigValue, | |
| 15 | + tryParse, | |
| 16 | + uuidv4, | |
| 17 | + mergeObjectWithYaml, | |
| 18 | + excludeKeysByYaml, | |
| 19 | + color, | |
| 20 | +} from '../../util.js'; | |
| 21 | +import { | |
| 22 | + convertClaudeMessages, | |
| 23 | + convertGooglePrompt, | |
| 24 | + convertTextCompletionPrompt, | |
| 25 | + convertCohereMessages, | |
| 26 | + convertMistralMessages, | |
| 27 | + convertAI21Messages, | |
| 28 | + mergeMessages, | |
| 29 | +} from '../../prompt-converters.js'; | |
| 30 | + | |
| 31 | +import { readSecret, SECRET_KEYS } from '../secrets.js'; | |
| 32 | +import { | |
| 33 | + getTokenizerModel, | |
| 34 | + getSentencepiceTokenizer, | |
| 35 | + getTiktokenTokenizer, | |
| 36 | + sentencepieceTokenizers, | |
| 37 | + TEXT_COMPLETION_MODELS, | |
| 38 | +} from '../tokenizers.js'; | |
| 11 | 39 | |
| 12 | 40 | const API_OPENAI = 'https://api.openai.com/v1'; |
| 13 | 41 | const API_CLAUDE = 'https://api.anthropic.com/v1'; |
| @@ -42,43 +70,6 @@ function postProcessPrompt(messages, type, charName, userName) { | ||
| 42 | 70 | } |
| 43 | 71 | |
| 44 | 72 | /** |
| 45 | - * Ollama strikes back. Special boy #2's steaming routine. | |
| 46 | - * Wrap this abomination into proper SSE stream, again. | |
| 47 | - * @param {Response} jsonStream JSON stream | |
| 48 | - * @param {import('express').Request} request Express request | |
| 49 | - * @param {import('express').Response} response Express response | |
| 50 | - * @returns {Promise<any>} Nothing valuable | |
| 51 | - */ | |
| 52 | -async function parseCohereStream(jsonStream, request, response) { | |
| 53 | - try { | |
| 54 | - const stream = new CohereStream({ stream: jsonStream.body, eventShape: { type: 'json', messageTerminator: '\n' } }); | |
| 55 | - | |
| 56 | - for await (const json of stream.iterMessages()) { | |
| 57 | - if (json.message) { | |
| 58 | - const message = json.message || 'Unknown error'; | |
| 59 | - const chunk = { error: { message: message } }; | |
| 60 | - response.write(`data: ${JSON.stringify(chunk)}\n\n`); | |
| 61 | - } else if (json.event_type === 'text-generation') { | |
| 62 | - const text = json.text || ''; | |
| 63 | - const chunk = { choices: [{ text }] }; | |
| 64 | - response.write(`data: ${JSON.stringify(chunk)}\n\n`); | |
| 65 | - } | |
| 66 | - } | |
| 67 | - | |
| 68 | - console.log('Streaming request finished'); | |
| 69 | - response.write('data: [DONE]\n\n'); | |
| 70 | - response.end(); | |
| 71 | - } catch (error) { | |
| 72 | - console.log('Error forwarding streaming response:', error); | |
| 73 | - if (!response.headersSent) { | |
| 74 | - return response.status(500).send({ error: true }); | |
| 75 | - } else { | |
| 76 | - return response.end(); | |
| 77 | - } | |
| 78 | - } | |
| 79 | -} | |
| 80 | - | |
| 81 | -/** | |
| 82 | 73 | * Sends a request to Claude API. |
| 83 | 74 | * @param {express.Request} request Express request |
| 84 | 75 | * @param {express.Response} response Express response |
| @@ -161,7 +152,6 @@ async function sendClaudeRequest(request, response) { | ||
| 161 | 152 | 'x-api-key': apiKey, |
| 162 | 153 | ...additionalHeaders, |
| 163 | 154 | }, |
| 164 | - timeout: 0, | |
| 165 | 155 | }); |
| 166 | 156 | |
| 167 | 157 | if (request.body.stream) { |
| @@ -174,6 +164,7 @@ async function sendClaudeRequest(request, response) { | ||
| 174 | 164 | return response.status(generateResponse.status).send({ error: true }); |
| 175 | 165 | } |
| 176 | 166 | |
| 167 | + /** @type {any} */ | |
| 177 | 168 | const generateResponseJson = await generateResponse.json(); |
| 178 | 169 | const responseText = generateResponseJson?.content?.[0]?.text || ''; |
| 179 | 170 | console.log('Claude response:', generateResponseJson); |
| @@ -221,7 +212,6 @@ async function sendScaleRequest(request, response) { | ||
| 221 | 212 | 'Content-Type': 'application/json', |
| 222 | 213 | 'Authorization': `Basic ${apiKey}`, |
| 223 | 214 | }, |
| 224 | - timeout: 0, | |
| 225 | 215 | }); |
| 226 | 216 | |
| 227 | 217 | if (!generateResponse.ok) { |
| @@ -229,6 +219,7 @@ async function sendScaleRequest(request, response) { | ||
| 229 | 219 | return response.status(500).send({ error: true }); |
| 230 | 220 | } |
| 231 | 221 | |
| 222 | + /** @type {any} */ | |
| 232 | 223 | const generateResponseJson = await generateResponse.json(); |
| 233 | 224 | console.log('Scale response:', generateResponseJson); |
| 234 | 225 | |
| @@ -344,7 +335,6 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 344 | 335 | 'Content-Type': 'application/json', |
| 345 | 336 | }, |
| 346 | 337 | signal: controller.signal, |
| 347 | - timeout: 0, | |
| 348 | 338 | }); |
| 349 | 339 | // have to do this because of their busted ass streaming endpoint |
| 350 | 340 | if (stream) { |
| @@ -363,6 +353,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 363 | 353 | return response.status(generateResponse.status).send({ error: true }); |
| 364 | 354 | } |
| 365 | 355 | |
| 356 | + /** @type {any} */ | |
| 366 | 357 | const generateResponseJson = await generateResponse.json(); |
| 367 | 358 | |
| 368 | 359 | const candidates = generateResponseJson?.candidates; |
| @@ -626,7 +617,7 @@ async function sendCohereRequest(request, response) { | ||
| 626 | 617 | } |
| 627 | 618 | } |
| 628 | 619 | |
| 629 | 620 | export const router = express.Router(); |
| 630 | 621 | |
| 631 | 622 | router.post('/status', jsonParser, async function (request, response_getstatus_openai) { |
| 632 | 623 | if (!request.body) return response_getstatus_openai.sendStatus(400); |
| @@ -685,6 +676,7 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o | ||
| 685 | 676 | }); |
| 686 | 677 | |
| 687 | 678 | if (response.ok) { |
| 679 | + /** @type {any} */ | |
| 688 | 680 | const data = await response.json(); |
| 689 | 681 | response_getstatus_openai.send(data); |
| 690 | 682 | |
| @@ -988,7 +980,6 @@ router.post('/generate', jsonParser, function (request, response) { | ||
| 988 | 980 | }, |
| 989 | 981 | body: JSON.stringify(requestBody), |
| 990 | 982 | signal: controller.signal, |
| 991 | - timeout: 0, | |
| 992 | 983 | }; |
| 993 | 984 | |
| 994 | 985 | console.log(requestBody); |
| @@ -1014,6 +1005,7 @@ router.post('/generate', jsonParser, function (request, response) { | ||
| 1014 | 1005 | } |
| 1015 | 1006 | |
| 1016 | 1007 | if (fetchResponse.ok) { |
| 1008 | + /** @type {any} */ | |
| 1017 | 1009 | let json = await fetchResponse.json(); |
| 1018 | 1010 | response.send(json); |
| 1019 | 1011 | console.log(json); |
| @@ -1069,6 +1061,3 @@ router.post('/generate', jsonParser, function (request, response) { | ||
| 1069 | 1061 | } |
| 1070 | 1062 | }); |
| 1071 | 1063 | |
| 1072 | -module.exports = { | |
| 1073 | - router, | |
| 1074 | -}; | |
| @@ -1,13 +1,13 @@ | ||
| 1 | -const express = require('express'); | |
| 1 | +import fs from 'node:fs'; | |
| 2 | -const fetch = require('node-fetch').default; | |
| 2 | +import express from 'express'; | |
| 3 | -const fs = require('fs'); | |
| 3 | +import fetch from 'node-fetch'; | |
| 4 | 4 | |
| 5 | 5 | constimport { jsonParser, urlencodedParser } =from require('../../express-common.js'); |
| 6 | 6 | constimport { forwardFetchResponse, delay } =from require('../../util.js'); |
| 7 | 7 | constimport { getOverrideHeaders, setAdditionalHeaders, setAdditionalHeadersByType } =from require('../../additional-headers.js'); |
| 8 | 8 | constimport { TEXTGEN_TYPES } =from require('../../constants.js'); |
| 9 | 9 | |
| 10 | 10 | export const router = express.Router(); |
| 11 | 11 | |
| 12 | 12 | router.post('/generate', jsonParser, async function (request, response_generate) { |
| 13 | 13 | if (!request.body) return response_generate.sendStatus(400); |
| @@ -96,7 +96,7 @@ router.post('/generate', jsonParser, async function (request, response_generate) | ||
| 96 | 96 | for (let i = 0; i < MAX_RETRIES; i++) { |
| 97 | 97 | try { |
| 98 | 98 | const url = request.body.streaming ? `${request.body.api_server}/extra/generate/stream` : `${request.body.api_server}/v1/generate`; |
| 99 | 99 | const response = await fetch(url, { method: 'POST', timeout: 0, ...args }); |
| 100 | 100 | |
| 101 | 101 | if (request.body.streaming) { |
| 102 | 102 | // Pipe remote SSE stream to Express response |
| @@ -156,6 +156,7 @@ router.post('/status', jsonParser, async function (request, response) { | ||
| 156 | 156 | |
| 157 | 157 | const result = {}; |
| 158 | 158 | |
| 159 | + /** @type {any} */ | |
| 159 | 160 | const [koboldUnitedResponse, koboldExtraResponse, koboldModelResponse] = await Promise.all([ |
| 160 | 161 | // We catch errors both from the response not having a successful HTTP status and from JSON parsing failing |
| 161 | 162 | |
| @@ -237,5 +238,3 @@ router.post('/transcribe-audio', urlencodedParser, async function (request, resp | ||
| 237 | 238 | response.status(500).send('Internal server error'); |
| 238 | 239 | } |
| 239 | 240 | }); |
| 240 | - | |
| 241 | -module.exports = { router }; | |
| @@ -1,11 +1,10 @@ | ||
| 1 | 1 | constimport express =from require('express'); |
| 2 | 2 | constimport fetch =from require('node-fetch').default; |
| 3 | 3 | |
| 4 | 4 | constimport { jsonParser } =from require('../../express-common.js'); |
| 5 | +import { readSecret, SECRET_KEYS } from '../secrets.js'; | |
| 5 | 6 | |
| 6 | -const { readSecret, SECRET_KEYS } = require('../secrets'); | |
| 7 | +export const router = express.Router(); | |
| 7 | - | |
| 8 | -const router = express.Router(); | |
| 9 | 8 | |
| 10 | 9 | router.post('/generate', jsonParser, async function (request, response) { |
| 11 | 10 | if (!request.body) return response.sendStatus(400); |
| @@ -71,7 +70,6 @@ router.post('/generate', jsonParser, async function (request, response) { | ||
| 71 | 70 | 'Content-Type': 'application/json', |
| 72 | 71 | 'cookie': `_jwt=${cookie}`, |
| 73 | 72 | }, |
| 74 | - timeout: 0, | |
| 75 | 73 | body: JSON.stringify(body), |
| 76 | 74 | }); |
| 77 | 75 | |
| @@ -81,6 +79,7 @@ router.post('/generate', jsonParser, async function (request, response) { | ||
| 81 | 79 | return response.status(500).send({ error: { message: result.statusText } }); |
| 82 | 80 | } |
| 83 | 81 | |
| 82 | + /** @type {any} */ | |
| 84 | 83 | const data = await result.json(); |
| 85 | 84 | const output = data?.result?.data?.json?.outputs?.[0] || ''; |
| 86 | 85 | |
| @@ -97,5 +96,3 @@ router.post('/generate', jsonParser, async function (request, response) { | ||
| 97 | 96 | return response.sendStatus(500); |
| 98 | 97 | } |
| 99 | 98 | }); |
| 100 | - | |
| 101 | -module.exports = { router }; | |
| @@ -1,14 +1,23 @@ | ||
| 1 | -const express = require('express'); | |
| 1 | +import { Readable } from 'node:stream'; | |
| 2 | 2 | constimport fetch =from require('node-fetch').default; |
| 3 | 3 | constimport _express =from require('lodashexpress'); |
| 4 | -const Readable = require('stream').Readable; | |
| 4 | +import _ from 'lodash'; | |
| 5 | 5 | |
| 6 | 6 | constimport { jsonParser } =from require('../../express-common.js'); |
| 7 | -const { TEXTGEN_TYPES, TOGETHERAI_KEYS, OLLAMA_KEYS, INFERMATICAI_KEYS, OPENROUTER_KEYS, VLLM_KEYS, DREAMGEN_KEYS, FEATHERLESS_KEYS } = require('../../constants'); | |
| 7 | +import { | |
| 8 | -const { forwardFetchResponse, trimV1, getConfigValue } = require('../../util'); | |
| 8 | + TEXTGEN_TYPES, | |
| 9 | -const { setAdditionalHeaders } = require('../../additional-headers'); | |
| 9 | + TOGETHERAI_KEYS, | |
| 10 | - | |
| 10 | + OLLAMA_KEYS, | |
| 11 | -const router = express.Router(); | |
| 11 | + INFERMATICAI_KEYS, | |
| 12 | + OPENROUTER_KEYS, | |
| 13 | + VLLM_KEYS, | |
| 14 | + DREAMGEN_KEYS, | |
| 15 | + FEATHERLESS_KEYS, | |
| 16 | +} from '../../constants.js'; | |
| 17 | +import { forwardFetchResponse, trimV1, getConfigValue } from '../../util.js'; | |
| 18 | +import { setAdditionalHeaders } from '../../additional-headers.js'; | |
| 19 | + | |
| 20 | +export const router = express.Router(); | |
| 12 | 21 | |
| 13 | 22 | /** |
| 14 | 23 | * Special boy's steaming routine. Wrap this abomination into proper SSE stream. |
| @@ -19,6 +28,10 @@ const router = express.Router(); | ||
| 19 | 28 | */ |
| 20 | 29 | async function parseOllamaStream(jsonStream, request, response) { |
| 21 | 30 | try { |
| 31 | + if (!jsonStream.body) { | |
| 32 | + throw new Error('No body in the response'); | |
| 33 | + } | |
| 34 | + | |
| 22 | 35 | let partialData = ''; |
| 23 | 36 | jsonStream.body.on('data', (data) => { |
| 24 | 37 | const chunk = data.toString(); |
| @@ -144,6 +157,7 @@ router.post('/status', jsonParser, async function (request, response) { | ||
| 144 | 157 | return response.status(400); |
| 145 | 158 | } |
| 146 | 159 | |
| 160 | + /** @type {any} */ | |
| 147 | 161 | let data = await modelsReply.json(); |
| 148 | 162 | |
| 149 | 163 | if (request.body.legacy_api) { |
| @@ -181,6 +195,7 @@ router.post('/status', jsonParser, async function (request, response) { | ||
| 181 | 195 | const modelInfoReply = await fetch(modelInfoUrl, args); |
| 182 | 196 | |
| 183 | 197 | if (modelInfoReply.ok) { |
| 198 | + /** @type {any} */ | |
| 184 | 199 | const modelInfo = await modelInfoReply.json(); |
| 185 | 200 | console.log('Ooba model info:', modelInfo); |
| 186 | 201 | |
| @@ -197,6 +212,7 @@ router.post('/status', jsonParser, async function (request, response) { | ||
| 197 | 212 | const modelInfoReply = await fetch(modelInfoUrl, args); |
| 198 | 213 | |
| 199 | 214 | if (modelInfoReply.ok) { |
| 215 | + /** @type {any} */ | |
| 200 | 216 | const modelInfo = await modelInfoReply.json(); |
| 201 | 217 | console.log('Tabby model info:', modelInfo); |
| 202 | 218 | |
| @@ -350,6 +366,7 @@ router.post('/generate', jsonParser, async function (request, response) { | ||
| 350 | 366 | const completionsReply = await fetch(url, args); |
| 351 | 367 | |
| 352 | 368 | if (completionsReply.ok) { |
| 369 | + /** @type {any} */ | |
| 353 | 370 | const data = await completionsReply.json(); |
| 354 | 371 | console.log('Endpoint response:', data); |
| 355 | 372 | |
| @@ -406,7 +423,6 @@ ollama.post('/download', jsonParser, async function (request, response) { | ||
| 406 | 423 | name: name, |
| 407 | 424 | stream: false, |
| 408 | 425 | }), |
| 409 | - timeout: 0, | |
| 410 | 426 | }); |
| 411 | 427 | |
| 412 | 428 | if (!fetchResponse.ok) { |
| @@ -439,7 +455,6 @@ ollama.post('/caption-image', jsonParser, async function (request, response) { | ||
| 439 | 455 | images: [request.body.image], |
| 440 | 456 | stream: false, |
| 441 | 457 | }), |
| 442 | - timeout: 0, | |
| 443 | 458 | }); |
| 444 | 459 | |
| 445 | 460 | if (!fetchResponse.ok) { |
| @@ -447,6 +462,7 @@ ollama.post('/caption-image', jsonParser, async function (request, response) { | ||
| 447 | 462 | return response.status(500).send({ error: true }); |
| 448 | 463 | } |
| 449 | 464 | |
| 465 | + /** @type {any} */ | |
| 450 | 466 | const data = await fetchResponse.json(); |
| 451 | 467 | console.log('Ollama caption response:', data); |
| 452 | 468 | |
| @@ -478,7 +494,6 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) { | ||
| 478 | 494 | const fetchResponse = await fetch(`${baseUrl}/completion`, { |
| 479 | 495 | method: 'POST', |
| 480 | 496 | headers: { 'Content-Type': 'application/json' }, |
| 481 | - timeout: 0, | |
| 482 | 497 | body: JSON.stringify({ |
| 483 | 498 | prompt: `USER:[img-1]${String(request.body.prompt).trim()}\nASSISTANT:`, |
| 484 | 499 | image_data: [{ data: request.body.image, id: 1 }], |
| @@ -493,6 +508,7 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) { | ||
| 493 | 508 | return response.status(500).send({ error: true }); |
| 494 | 509 | } |
| 495 | 510 | |
| 511 | + /** @type {any} */ | |
| 496 | 512 | const data = await fetchResponse.json(); |
| 497 | 513 | console.log('LlamaCpp caption response:', data); |
| 498 | 514 | |
| @@ -522,7 +538,6 @@ llamacpp.post('/props', jsonParser, async function (request, response) { | ||
| 522 | 538 | |
| 523 | 539 | const fetchResponse = await fetch(`${baseUrl}/props`, { |
| 524 | 540 | method: 'GET', |
| 525 | - timeout: 0, | |
| 526 | 541 | }); |
| 527 | 542 | |
| 528 | 543 | if (!fetchResponse.ok) { |
| @@ -557,7 +572,6 @@ llamacpp.post('/slots', jsonParser, async function (request, response) { | ||
| 557 | 572 | if (request.body.action === 'info') { |
| 558 | 573 | fetchResponse = await fetch(`${baseUrl}/slots`, { |
| 559 | 574 | method: 'GET', |
| 560 | - timeout: 0, | |
| 561 | 575 | }); |
| 562 | 576 | } else { |
| 563 | 577 | if (!/^\d+$/.test(request.body.id_slot)) { |
| @@ -570,7 +584,6 @@ llamacpp.post('/slots', jsonParser, async function (request, response) { | ||
| 570 | 584 | fetchResponse = await fetch(`${baseUrl}/slots/${request.body.id_slot}?action=${request.body.action}`, { |
| 571 | 585 | method: 'POST', |
| 572 | 586 | headers: { 'Content-Type': 'application/json' }, |
| 573 | - timeout: 0, | |
| 574 | 587 | body: JSON.stringify({ |
| 575 | 588 | filename: request.body.action !== 'erase' ? `${request.body.filename}` : undefined, |
| 576 | 589 | }), |
| @@ -614,6 +627,7 @@ tabby.post('/download', jsonParser, async function (request, response) { | ||
| 614 | 627 | }); |
| 615 | 628 | |
| 616 | 629 | if (permissionResponse.ok) { |
| 630 | + /** @type {any} */ | |
| 617 | 631 | const permissionJson = await permissionResponse.json(); |
| 618 | 632 | |
| 619 | 633 | if (permissionJson['permission'] !== 'admin') { |
| @@ -641,5 +655,3 @@ tabby.post('/download', jsonParser, async function (request, response) { | ||
| 641 | 655 | router.use('/ollama', ollama); |
| 642 | 656 | router.use('/llamacpp', llamacpp); |
| 643 | 657 | router.use('/tabby', tabby); |
| 644 | - | |
| 645 | -module.exports = { router }; | |
| @@ -1,13 +1,14 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 4 | -const sanitize = require('sanitize-filename'); | |
| 5 | 3 | |
| 6 | -const { jsonParser, urlencodedParser } = require('../express-common'); | |
| 4 | +import express from 'express'; | |
| 7 | -const { invalidateThumbnail } = require('./thumbnails'); | |
| 5 | +import sanitize from 'sanitize-filename'; | |
| 8 | -const { getImages } = require('../util'); | |
| 9 | 6 | |
| 10 | -const router = express.Router(); | |
| 7 | +import { jsonParser, urlencodedParser } from '../express-common.js'; | |
| 8 | +import { invalidateThumbnail } from './thumbnails.js'; | |
| 9 | +import { getImages } from '../util.js'; | |
| 10 | + | |
| 11 | +export const router = express.Router(); | |
| 11 | 12 | |
| 12 | 13 | router.post('/all', jsonParser, function (request, response) { |
| 13 | 14 | var images = getImages(request.user.directories.backgrounds); |
| @@ -72,5 +73,3 @@ router.post('/upload', urlencodedParser, function (request, response) { | ||
| 72 | 73 | response.sendStatus(500); |
| 73 | 74 | } |
| 74 | 75 | }); |
| 75 | - | |
| 76 | -module.exports = { router }; | |
| @@ -1,23 +1,23 @@ | ||
| 1 | 1 | constimport express =from require('express'); |
| 2 | 2 | constimport { jsonParser } =from require('../express-common.js'); |
| 3 | +import { getPipeline, getRawImage } from '../transformers.mjs'; | |
| 3 | 4 | |
| 4 | 5 | const TASK = 'image-to-text'; |
| 5 | 6 | |
| 6 | 7 | export const router = express.Router(); |
| 7 | 8 | |
| 8 | 9 | router.post('/', jsonParser, async (req, res) => { |
| 9 | 10 | try { |
| 10 | 11 | const { image } = req.body; |
| 11 | 12 | |
| 12 | 13 | const modulerawImage = await importgetRawImage('../transformers.mjs'image); |
| 13 | - const rawImage = await module.default.getRawImage(image); | |
| 14 | 14 | |
| 15 | 15 | if (!rawImage) { |
| 16 | 16 | console.log('Failed to parse captioned image'); |
| 17 | 17 | return res.sendStatus(400); |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | 20 | const pipe = await module.default.getPipeline(TASK); |
| 21 | 21 | const result = await pipe(rawImage); |
| 22 | 22 | const text = result[0].generated_text; |
| 23 | 23 | console.log('Image caption:', text); |
| @@ -28,5 +28,3 @@ router.post('/', jsonParser, async (req, res) => { | ||
| 28 | 28 | return res.sendStatus(500); |
| 29 | 29 | } |
| 30 | 30 | }); |
| 31 | - | |
| 32 | -module.exports = { router }; | |
| @@ -1,24 +1,25 @@ | ||
| 1 | 1 | constimport path =from require('node:path'); |
| 2 | 2 | constimport fs =from require('node:fs'); |
| 3 | -const fsPromises = require('fs').promises; | |
| 3 | +import { promises as fsPromises } from 'node:fs'; | |
| 4 | 4 | constimport readline =from require('node:readline'); |
| 5 | -const express = require('express'); | |
| 5 | +import { Buffer } from 'node:buffer'; | |
| 6 | -const sanitize = require('sanitize-filename'); | |
| 6 | + | |
| 7 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 7 | +import express from 'express'; | |
| 8 | -const yaml = require('yaml'); | |
| 8 | +import sanitize from 'sanitize-filename'; | |
| 9 | -const _ = require('lodash'); | |
| 9 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 10 | -const mime = require('mime-types'); | |
| 10 | +import yaml from 'yaml'; | |
| 11 | - | |
| 11 | +import _ from 'lodash'; | |
| 12 | -const jimp = require('jimp'); | |
| 12 | +import mime from 'mime-types'; | |
| 13 | - | |
| 13 | +import jimp from 'jimp'; | |
| 14 | -const { AVATAR_WIDTH, AVATAR_HEIGHT } = require('../constants'); | |
| 14 | + | |
| 15 | 15 | constimport { jsonParserAVATAR_WIDTH, urlencodedParserAVATAR_HEIGHT } =from require('../express-commonconstants.js'); |
| 16 | 16 | constimport { deepMerge, humanizedISO8601DateTime, tryParsejsonParser, extractFileFromZipBufferurlencodedParser } =from require('../utilexpress-common.js'); |
| 17 | 17 | constimport { TavernCardValidatordeepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer } =from require('../validator/TavernCardValidatorutil.js'); |
| 18 | 18 | constimport characterCardParser{ =TavernCardValidator require(} from '../character-card-parservalidator/TavernCardValidator.js'); |
| 19 | 19 | constimport { readWorldInfoFileparse, write } =from require('../worldinfocharacter-card-parser.js'); |
| 20 | 20 | constimport { invalidateThumbnailreadWorldInfoFile } =from require('./thumbnailsworldinfo.js'); |
| 21 | 21 | constimport { importRisuSpritesinvalidateThumbnail } =from require('./spritesthumbnails.js'); |
| 22 | +import { importRisuSprites } from './sprites.js'; | |
| 22 | 23 | const defaultAvatarPath = './public/img/ai4.png'; |
| 23 | 24 | |
| 24 | 25 | // KV-store for parsed character data |
| @@ -37,7 +38,7 @@ async function readCharacterData(inputFile, inputFormat = 'png') { | ||
| 37 | 38 | return characterDataCache.get(cacheKey); |
| 38 | 39 | } |
| 39 | 40 | |
| 40 | 41 | const result = characterCardParser.parse(inputFile, inputFormat); |
| 41 | 42 | characterDataCache.set(cacheKey, result); |
| 42 | 43 | return result; |
| 43 | 44 | } |
| @@ -76,7 +77,7 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u | ||
| 76 | 77 | const inputImage = await getInputImage(); |
| 77 | 78 | |
| 78 | 79 | // Get the chunks |
| 79 | 80 | const outputImage = characterCardParser.write(inputImage, data); |
| 80 | 81 | const outputImagePath = path.join(request.user.directories.characters, `${outputFile}.png`); |
| 81 | 82 | |
| 82 | 83 | writeFileAtomicSync(outputImagePath, outputImage); |
| @@ -188,7 +189,7 @@ const calculateDataSize = (data) => { | ||
| 188 | 189 | * processCharacter - Process a given character, read its data and calculate its statistics. |
| 189 | 190 | * |
| 190 | 191 | * @param {string} item The name of the character. |
| 191 | 192 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 192 | 193 | * @return {Promise<object>} A Promise that resolves when the character processing is done. |
| 193 | 194 | */ |
| 194 | 195 | const processCharacter = async (item, directories) => { |
| @@ -232,7 +233,7 @@ const processCharacter = async (item, directories) => { | ||
| 232 | 233 | /** |
| 233 | 234 | * Convert a character object to Spec V2 format. |
| 234 | 235 | * @param {object} jsonObject Character object |
| 235 | 236 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 236 | 237 | * @param {boolean} hoistDate Will set the chat and create_date fields to the current date if they are missing |
| 237 | 238 | * @returns {object} Character object in Spec V2 format |
| 238 | 239 | */ |
| @@ -252,7 +253,7 @@ function getCharaCardV2(jsonObject, directories, hoistDate = true) { | ||
| 252 | 253 | /** |
| 253 | 254 | * Convert a character object to Spec V2 format. |
| 254 | 255 | * @param {object} char Character object |
| 255 | 256 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 256 | 257 | * @returns {object} Character object in Spec V2 format |
| 257 | 258 | */ |
| 258 | 259 | function convertToV2(char, directories) { |
| @@ -342,7 +343,7 @@ function readFromV2(char) { | ||
| 342 | 343 | /** |
| 343 | 344 | * Format character data to Spec V2 format. |
| 344 | 345 | * @param {object} data Character data |
| 345 | 346 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 346 | 347 | * @returns |
| 347 | 348 | */ |
| 348 | 349 | function charaFormatData(data, directories) { |
| @@ -715,7 +716,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) { | ||
| 715 | 716 | return ''; |
| 716 | 717 | } |
| 717 | 718 | |
| 718 | 719 | export const router = express.Router(); |
| 719 | 720 | |
| 720 | 721 | router.post('/create', urlencodedParser, async function (request, response) { |
| 721 | 722 | try { |
| @@ -1081,7 +1082,7 @@ router.post('/chats', jsonParser, async function (request, response) { | ||
| 1081 | 1082 | /** |
| 1082 | 1083 | * Gets the name for the uploaded PNG file. |
| 1083 | 1084 | * @param {string} file File name |
| 1084 | 1085 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 1085 | 1086 | * @returns {string} - The name for the uploaded PNG file |
| 1086 | 1087 | */ |
| 1087 | 1088 | function getPngName(file, directories) { |
| @@ -1231,5 +1232,3 @@ router.post('/export', jsonParser, async function (request, response) { | ||
| 1231 | 1232 | response.sendStatus(500); |
| 1232 | 1233 | } |
| 1233 | 1234 | }); |
| 1234 | - | |
| 1235 | -module.exports = { router }; | |
| @@ -1,13 +1,15 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | 3 | constimport readline =from require('node:readline'); |
| 4 | -const express = require('express'); | |
| 4 | +import process from 'node:process'; | |
| 5 | -const sanitize = require('sanitize-filename'); | |
| 6 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 7 | -const _ = require('lodash'); | |
| 8 | 5 | |
| 9 | -const { jsonParser, urlencodedParser } = require('../express-common'); | |
| 6 | +import express from 'express'; | |
| 10 | -const { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } = require('../util'); | |
| 7 | +import sanitize from 'sanitize-filename'; | |
| 8 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 9 | +import _ from 'lodash'; | |
| 10 | + | |
| 11 | +import { jsonParser, urlencodedParser } from '../express-common.js'; | |
| 12 | +import { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } from '../util.js'; | |
| 11 | 13 | |
| 12 | 14 | /** |
| 13 | 15 | * Saves a chat to the backups directory. |
| @@ -188,7 +190,7 @@ function flattenChubChat(userName, characterName, lines) { | ||
| 188 | 190 | return (lines ?? []).map(convert).join('\n'); |
| 189 | 191 | } |
| 190 | 192 | |
| 191 | 193 | export const router = express.Router(); |
| 192 | 194 | |
| 193 | 195 | router.post('/save', jsonParser, function (request, response) { |
| 194 | 196 | try { |
| @@ -514,5 +516,3 @@ router.post('/group/save', jsonParser, (request, response) => { | ||
| 514 | 516 | getBackupFunction(request.user.profile.handle)(request.user.directories.backups, String(id), jsonlData); |
| 515 | 517 | return response.send({ ok: true }); |
| 516 | 518 | }); |
| 517 | - | |
| 518 | -module.exports = { router }; | |
| @@ -1,9 +1,11 @@ | ||
| 1 | 1 | constimport express =from require('express'); |
| 2 | -const { jsonParser } = require('../express-common'); | |
| 2 | + | |
| 3 | +import { getPipeline } from '../transformers.mjs'; | |
| 4 | +import { jsonParser } from '../express-common.js'; | |
| 3 | 5 | |
| 4 | 6 | const TASK = 'text-classification'; |
| 5 | 7 | |
| 6 | 8 | export const router = express.Router(); |
| 7 | 9 | |
| 8 | 10 | /** |
| 9 | 11 | * @type {Map<string, object>} Cache for classification results |
| @@ -12,8 +14,7 @@ const cacheObject = new Map(); | ||
| 12 | 14 | |
| 13 | 15 | router.post('/labels', jsonParser, async (req, res) => { |
| 14 | 16 | try { |
| 15 | 17 | const modulepipe = await importgetPipeline('../transformers.mjs'TASK); |
| 16 | - const pipe = await module.default.getPipeline(TASK); | |
| 17 | 18 | const result = Object.keys(pipe.model.config.label2id); |
| 18 | 19 | return res.json({ labels: result }); |
| 19 | 20 | } catch (error) { |
| @@ -35,8 +36,7 @@ router.post('/', jsonParser, async (req, res) => { | ||
| 35 | 36 | if (cacheObject.has(text)) { |
| 36 | 37 | return cacheObject.get(text); |
| 37 | 38 | } else { |
| 38 | 39 | const modulepipe = await importgetPipeline('../transformers.mjs'TASK); |
| 39 | - const pipe = await module.default.getPipeline(TASK); | |
| 40 | 40 | const result = await pipe(text, { topk: 5 }); |
| 41 | 41 | result.sort((a, b) => b.score - a.score); |
| 42 | 42 | cacheObject.set(text, result); |
| @@ -54,5 +54,3 @@ router.post('/', jsonParser, async (req, res) => { | ||
| 54 | 54 | return res.sendStatus(500); |
| 55 | 55 | } |
| 56 | 56 | }); |
| 57 | - | |
| 58 | -module.exports = { router }; | |
| @@ -1,16 +1,21 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 3 | +import process from 'node:process'; | |
| 4 | -const fetch = require('node-fetch').default; | |
| 4 | +import { Buffer } from 'node:buffer'; | |
| 5 | -const sanitize = require('sanitize-filename'); | |
| 5 | + | |
| 6 | -const { getConfigValue, color } = require('../util'); | |
| 6 | +import express from 'express'; | |
| 7 | -const { jsonParser } = require('../express-common'); | |
| 7 | +import fetch from 'node-fetch'; | |
| 8 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 8 | +import sanitize from 'sanitize-filename'; | |
| 9 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 10 | + | |
| 11 | +import { getConfigValue, color } from '../util.js'; | |
| 12 | +import { jsonParser } from '../express-common.js'; | |
| 13 | +import { write } from '../character-card-parser.js'; | |
| 14 | + | |
| 9 | 15 | const contentDirectory = path.join(process.cwd(), 'default/content'); |
| 10 | 16 | const scaffoldDirectory = path.join(process.cwd(), 'default/scaffold'); |
| 11 | 17 | const contentIndexPath = path.join(contentDirectory, 'index.json'); |
| 12 | 18 | const scaffoldIndexPath = path.join(scaffoldDirectory, 'index.json'); |
| 13 | -const characterCardParser = require('../character-card-parser.js'); | |
| 14 | 19 | |
| 15 | 20 | const WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES = getConfigValue('whitelistImportDomains', []); |
| 16 | 21 | |
| @@ -26,7 +31,7 @@ const WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES = getConfigValue('whitelistImportDo | ||
| 26 | 31 | * @typedef {string} ContentType |
| 27 | 32 | * @enum {string} |
| 28 | 33 | */ |
| 29 | 34 | export const CONTENT_TYPES = { |
| 30 | 35 | SETTINGS: 'settings', |
| 31 | 36 | CHARACTER: 'character', |
| 32 | 37 | SPRITES: 'sprites', |
| @@ -48,10 +53,10 @@ const CONTENT_TYPES = { | ||
| 48 | 53 | |
| 49 | 54 | /** |
| 50 | 55 | * Gets the default presets from the content directory. |
| 51 | 56 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 52 | 57 | * @returns {object[]} Array of default presets |
| 53 | 58 | */ |
| 54 | 59 | export function getDefaultPresets(directories) { |
| 55 | 60 | try { |
| 56 | 61 | const contentIndex = getContentIndex(); |
| 57 | 62 | const presets = []; |
| @@ -76,7 +81,7 @@ function getDefaultPresets(directories) { | ||
| 76 | 81 | * @param {string} filename Name of the file to get |
| 77 | 82 | * @returns {object | null} JSON object or null if the file doesn't exist |
| 78 | 83 | */ |
| 79 | 84 | export function getDefaultPresetFile(filename) { |
| 80 | 85 | try { |
| 81 | 86 | const contentPath = path.join(contentDirectory, filename); |
| 82 | 87 | |
| @@ -95,7 +100,7 @@ function getDefaultPresetFile(filename) { | ||
| 95 | 100 | /** |
| 96 | 101 | * Seeds content for a user. |
| 97 | 102 | * @param {ContentItem[]} contentIndex Content index |
| 98 | 103 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 99 | 104 | * @param {string[]} forceCategories List of categories to force check (even if content check is skipped) |
| 100 | 105 | * @returns {Promise<boolean>} Whether any content was added |
| 101 | 106 | */ |
| @@ -154,11 +159,11 @@ async function seedContentForUser(contentIndex, directories, forceCategories) { | ||
| 154 | 159 | |
| 155 | 160 | /** |
| 156 | 161 | * Checks for new content and seeds it for all users. |
| 157 | 162 | * @param {import('../users.js').UserDirectoryList[]} directoriesList List of user directories |
| 158 | 163 | * @param {string[]} forceCategories List of categories to force check (even if content check is skipped) |
| 159 | 164 | * @returns {Promise<void>} |
| 160 | 165 | */ |
| 161 | 166 | export async function checkForNewContent(directoriesList, forceCategories = []) { |
| 162 | 167 | try { |
| 163 | 168 | const contentCheckSkip = getConfigValue('skipContentCheck', false); |
| 164 | 169 | if (contentCheckSkip && forceCategories?.length === 0) { |
| @@ -224,7 +229,7 @@ function getContentIndex() { | ||
| 224 | 229 | * @param {'json'|'string'|'raw'} format Format of content |
| 225 | 230 | * @returns {string[]|Buffer[]} Array of content |
| 226 | 231 | */ |
| 227 | 232 | export function getContentOfType(type, format) { |
| 228 | 233 | const contentIndex = getContentIndex(); |
| 229 | 234 | const indexItems = contentIndex.filter((item) => item.type === type && item.folder); |
| 230 | 235 | const files = []; |
| @@ -256,7 +261,7 @@ function getContentOfType(type, format) { | ||
| 256 | 261 | /** |
| 257 | 262 | * Gets the target directory for the specified asset type. |
| 258 | 263 | * @param {ContentType} type Asset type |
| 259 | 264 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 260 | 265 | * @returns {string | null} Target directory |
| 261 | 266 | */ |
| 262 | 267 | function getTargetByType(type, directories) { |
| @@ -375,6 +380,7 @@ async function downloadPygmalionCharacter(id) { | ||
| 375 | 380 | throw new Error('Failed to download character'); |
| 376 | 381 | } |
| 377 | 382 | |
| 383 | + /** @type {any} */ | |
| 378 | 384 | const jsonData = await result.json(); |
| 379 | 385 | const characterData = jsonData?.character; |
| 380 | 386 | |
| @@ -394,7 +400,7 @@ async function downloadPygmalionCharacter(id) { | ||
| 394 | 400 | const avatarResult = await fetch(avatarUrl); |
| 395 | 401 | const avatarBuffer = await avatarResult.buffer(); |
| 396 | 402 | |
| 397 | 403 | const cardBuffer = characterCardParser.write(avatarBuffer, JSON.stringify(characterData)); |
| 398 | 404 | |
| 399 | 405 | return { |
| 400 | 406 | buffer: cardBuffer, |
| @@ -467,6 +473,7 @@ async function downloadJannyCharacter(uuid) { | ||
| 467 | 473 | }); |
| 468 | 474 | |
| 469 | 475 | if (result.ok) { |
| 476 | + /** @type {any} */ | |
| 470 | 477 | const downloadResult = await result.json(); |
| 471 | 478 | if (downloadResult.status === 'ok') { |
| 472 | 479 | const imageResult = await fetch(downloadResult.downloadUrl); |
| @@ -618,7 +625,7 @@ function isHostWhitelisted(host) { | ||
| 618 | 625 | return WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES.includes(host); |
| 619 | 626 | } |
| 620 | 627 | |
| 621 | 628 | export const router = express.Router(); |
| 622 | 629 | |
| 623 | 630 | router.post('/importURL', jsonParser, async (request, response) => { |
| 624 | 631 | if (!request.body.url) { |
| @@ -753,12 +760,3 @@ router.post('/importUUID', jsonParser, async (request, response) => { | ||
| 753 | 760 | return response.sendStatus(500); |
| 754 | 761 | } |
| 755 | 762 | }); |
| 756 | - | |
| 757 | -module.exports = { | |
| 758 | - CONTENT_TYPES, | |
| 759 | - checkForNewContent, | |
| 760 | - getDefaultPresets, | |
| 761 | - getDefaultPresetFile, | |
| 762 | - getContentOfType, | |
| 763 | - router, | |
| 764 | -}; | |
| @@ -1,10 +1,12 @@ | ||
| 1 | 1 | constimport path =from require('node:path'); |
| 2 | 2 | constimport fs =from require('node:fs'); |
| 3 | -const express = require('express'); | |
| 3 | + | |
| 4 | -const { default: simpleGit } = require('simple-git'); | |
| 4 | +import express from 'express'; | |
| 5 | 5 | constimport sanitize =from require('sanitize-filename'); |
| 6 | 6 | constimport { PUBLIC_DIRECTORIESdefault as simpleGit } =from require('../constantssimple-git'); |
| 7 | -const { jsonParser } = require('../express-common'); | |
| 7 | + | |
| 8 | +import { PUBLIC_DIRECTORIES } from '../constants.js'; | |
| 9 | +import { jsonParser } from '../express-common.js'; | |
| 8 | 10 | |
| 9 | 11 | /** |
| 10 | 12 | * This function extracts the extension information from the manifest file. |
| @@ -47,7 +49,7 @@ async function checkIfRepoIsUpToDate(extensionPath) { | ||
| 47 | 49 | }; |
| 48 | 50 | } |
| 49 | 51 | |
| 50 | 52 | export const router = express.Router(); |
| 51 | 53 | |
| 52 | 54 | /** |
| 53 | 55 | * HTTP POST handler function to clone a git repository from a provided URL, read the extension manifest, |
| @@ -240,5 +242,3 @@ router.get('/discover', jsonParser, function (request, response) { | ||
| 240 | 242 | |
| 241 | 243 | return response.send(extensions); |
| 242 | 244 | }); |
| 243 | - | |
| 244 | -module.exports = { router }; | |
| @@ -1,12 +1,15 @@ | ||
| 1 | 1 | constimport path =from require('node:path'); |
| 2 | 2 | constimport fs =from require('node:fs'); |
| 3 | -const writeFileSyncAtomic = require('write-file-atomic').sync; | |
| 3 | + | |
| 4 | 4 | constimport express =from require('express'); |
| 5 | 5 | constimport sanitize =from require('sanitize-filename'); |
| 6 | -const router = express.Router(); | |
| 6 | +import { sync as writeFileSyncAtomic } from 'write-file-atomic'; | |
| 7 | -const { validateAssetFileName } = require('./assets'); | |
| 7 | + | |
| 8 | 8 | constimport { jsonParservalidateAssetFileName } =from require('../express-commonassets.js'); |
| 9 | 9 | constimport { clientRelativePathjsonParser } =from require('../utilexpress-common.js'); |
| 10 | +import { clientRelativePath } from '../util.js'; | |
| 11 | + | |
| 12 | +export const router = express.Router(); | |
| 10 | 13 | |
| 11 | 14 | router.post('/sanitize-filename', jsonParser, async (request, response) => { |
| 12 | 15 | try { |
| @@ -97,5 +100,3 @@ router.post('/verify', jsonParser, async (request, response) => { | ||
| 97 | 100 | return response.sendStatus(500); |
| 98 | 101 | } |
| 99 | 102 | }); |
| 100 | - | |
| 101 | -module.exports = { router }; | |
| @@ -1,12 +1,13 @@ | ||
| 1 | -const { readSecret, SECRET_KEYS } = require('./secrets'); | |
| 1 | +import fetch from 'node-fetch'; | |
| 2 | -const fetch = require('node-fetch').default; | |
| 2 | +import express from 'express'; | |
| 3 | -const express = require('express'); | |
| 3 | + | |
| 4 | 4 | constimport { jsonParserreadSecret, SECRET_KEYS } =from require('../express-commonsecrets.js'); |
| 5 | 5 | constimport { GEMINI_SAFETYjsonParser } = from require('../constantsexpress-common.js'); |
| 6 | +import { GEMINI_SAFETY } from '../constants.js'; | |
| 6 | 7 | |
| 7 | 8 | const API_MAKERSUITE = 'https://generativelanguage.googleapis.com'; |
| 8 | 9 | |
| 9 | 10 | export const router = express.Router(); |
| 10 | 11 | |
| 11 | 12 | router.post('/caption-image', jsonParser, async (request, response) => { |
| 12 | 13 | try { |
| @@ -39,7 +40,6 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 39 | 40 | headers: { |
| 40 | 41 | 'Content-Type': 'application/json', |
| 41 | 42 | }, |
| 42 | - timeout: 0, | |
| 43 | 43 | }); |
| 44 | 44 | |
| 45 | 45 | if (!result.ok) { |
| @@ -48,6 +48,7 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 48 | 48 | return response.status(result.status).send({ error: true }); |
| 49 | 49 | } |
| 50 | 50 | |
| 51 | + /** @type {any} */ | |
| 51 | 52 | const data = await result.json(); |
| 52 | 53 | console.log('Multimodal captioning response', data); |
| 53 | 54 | |
| @@ -67,5 +68,3 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 67 | 68 | response.status(500).send('Internal server error'); |
| 68 | 69 | } |
| 69 | 70 | }); |
| 70 | - | |
| 71 | -module.exports = { router }; | |
| @@ -1,13 +1,14 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 4 | -const sanitize = require('sanitize-filename'); | |
| 5 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 6 | 3 | |
| 7 | -const { jsonParser } = require('../express-common'); | |
| 4 | +import express from 'express'; | |
| 8 | -const { humanizedISO8601DateTime } = require('../util'); | |
| 5 | +import sanitize from 'sanitize-filename'; | |
| 6 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 9 | 7 | |
| 10 | -const router = express.Router(); | |
| 8 | +import { jsonParser } from '../express-common.js'; | |
| 9 | +import { humanizedISO8601DateTime } from '../util.js'; | |
| 10 | + | |
| 11 | +export const router = express.Router(); | |
| 11 | 12 | |
| 12 | 13 | router.post('/all', jsonParser, (request, response) => { |
| 13 | 14 | const groups = []; |
| @@ -131,5 +132,3 @@ router.post('/delete', jsonParser, async (request, response) => { | ||
| 131 | 132 | |
| 132 | 133 | return response.send({ ok: true }); |
| 133 | 134 | }); |
| 134 | - | |
| 135 | -module.exports = { router }; | |
| @@ -1,14 +1,14 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport express =from require('express'); |
| 3 | 3 | constimport { AIHorde, ModelGenerationInputStableSamplers, ModelInterrogationFormTypes, HordeAsyncRequestStates } =from require('@zeldafan0225/ai_horde'); |
| 4 | 4 | constimport { getVersion, delay, Cache } =from require('../util.js'); |
| 5 | 5 | constimport { readSecret, SECRET_KEYS } =from require('./secrets.js'); |
| 6 | 6 | constimport { jsonParser } =from require('../express-common.js'); |
| 7 | 7 | |
| 8 | 8 | const ANONYMOUS_KEY = '0000000000'; |
| 9 | 9 | const HORDE_TEXT_MODEL_METADATA_URL = 'https://raw.githubusercontent.com/db0/AI-Horde-text-model-reference/main/db.json'; |
| 10 | 10 | const cache = new Cache(60 * 1000); |
| 11 | 11 | export const router = express.Router(); |
| 12 | 12 | |
| 13 | 13 | /** |
| 14 | 14 | * Returns the AIHorde client agent. |
| @@ -403,5 +403,3 @@ router.post('/generate-image', jsonParser, async (request, response) => { | ||
| 403 | 403 | return response.sendStatus(500); |
| 404 | 404 | } |
| 405 | 405 | }); |
| 406 | - | |
| 407 | -module.exports = { router }; | |
| @@ -1,10 +1,12 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 3 | +import { Buffer } from 'node:buffer'; | |
| 4 | -const sanitize = require('sanitize-filename'); | |
| 5 | 4 | |
| 6 | -const { jsonParser } = require('../express-common'); | |
| 5 | +import express from 'express'; | |
| 7 | -const { clientRelativePath, removeFileExtension, getImages } = require('../util'); | |
| 6 | +import sanitize from 'sanitize-filename'; | |
| 7 | + | |
| 8 | +import { jsonParser } from '../express-common.js'; | |
| 9 | +import { clientRelativePath, removeFileExtension, getImages } from '../util.js'; | |
| 8 | 10 | |
| 9 | 11 | /** |
| 10 | 12 | * Ensure the directory for the provided file path exists. |
| @@ -21,7 +23,7 @@ function ensureDirectoryExistence(filePath) { | ||
| 21 | 23 | fs.mkdirSync(dirname); |
| 22 | 24 | } |
| 23 | 25 | |
| 24 | 26 | export const router = express.Router(); |
| 25 | 27 | |
| 26 | 28 | /** |
| 27 | 29 | * Endpoint to handle image uploads. |
| @@ -66,7 +68,7 @@ router.post('/upload', jsonParser, async (request, response) => { | ||
| 66 | 68 | |
| 67 | 69 | ensureDirectoryExistence(pathToNewFile); |
| 68 | 70 | const imageBuffer = Buffer.from(base64Data, 'base64'); |
| 69 | 71 | await fs.promises.writeFile(pathToNewFile, new Uint8Array(imageBuffer)); |
| 70 | 72 | response.send({ path: clientRelativePath(request.user.directories.root, pathToNewFile) }); |
| 71 | 73 | } catch (error) { |
| 72 | 74 | console.log(error); |
| @@ -89,5 +91,3 @@ router.post('/list/:folder', (request, response) => { | ||
| 89 | 91 | return response.status(500).send({ error: 'Unable to retrieve files' }); |
| 90 | 92 | } |
| 91 | 93 | }); |
| 92 | - | |
| 93 | -module.exports = { router }; | |
| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | constimport path =from require('node:path'); |
| 2 | 2 | constimport express =from require('express'); |
| 3 | 3 | constimport sanitize =from require('sanitize-filename'); |
| 4 | 4 | constimport { sync as writeFileAtomicSync =} require(from 'write-file-atomic').sync; |
| 5 | 5 | |
| 6 | 6 | constimport { jsonParser } =from require('../express-common.js'); |
| 7 | 7 | |
| 8 | 8 | export const router = express.Router(); |
| 9 | 9 | |
| 10 | 10 | router.post('/save', jsonParser, (request, response) => { |
| 11 | 11 | if (!request.body || !request.body.name) { |
| @@ -17,5 +17,3 @@ router.post('/save', jsonParser, (request, response) => { | ||
| 17 | 17 | |
| 18 | 18 | return response.sendStatus(200); |
| 19 | 19 | }); |
| 20 | - | |
| 21 | -module.exports = { router }; | |
| @@ -1,9 +1,12 @@ | ||
| 1 | -const fetch = require('node-fetch').default; | |
| 1 | +import util from 'node:util'; | |
| 2 | -const express = require('express'); | |
| 2 | +import { Buffer } from 'node:buffer'; | |
| 3 | -const util = require('util'); | |
| 3 | + | |
| 4 | -const { readSecret, SECRET_KEYS } = require('./secrets'); | |
| 4 | +import fetch from 'node-fetch'; | |
| 5 | -const { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } = require('../util'); | |
| 5 | +import express from 'express'; | |
| 6 | -const { jsonParser } = require('../express-common'); | |
| 6 | + | |
| 7 | +import { readSecret, SECRET_KEYS } from './secrets.js'; | |
| 8 | +import { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } from '../util.js'; | |
| 9 | +import { jsonParser } from '../express-common.js'; | |
| 7 | 10 | |
| 8 | 11 | const API_NOVELAI = 'https://api.novelai.net'; |
| 9 | 12 | const TEXT_NOVELAI = 'https://text.novelai.net'; |
| @@ -110,7 +113,7 @@ function getRepPenaltyWhitelist(model) { | ||
| 110 | 113 | return null; |
| 111 | 114 | } |
| 112 | 115 | |
| 113 | 116 | export const router = express.Router(); |
| 114 | 117 | |
| 115 | 118 | router.post('/status', jsonParser, async function (req, res) { |
| 116 | 119 | if (!req.body) return res.sendStatus(400); |
| @@ -249,7 +252,7 @@ router.post('/generate', jsonParser, async function (req, res) { | ||
| 249 | 252 | try { |
| 250 | 253 | const baseURL = (req.body.model.includes('kayra') || req.body.model.includes('erato')) ? TEXT_NOVELAI : API_NOVELAI; |
| 251 | 254 | const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`; |
| 252 | 255 | const response = await fetch(url, { method: 'POST', timeout: 0, ...args }); |
| 253 | 256 | |
| 254 | 257 | if (req.body.streaming) { |
| 255 | 258 | // Pipe remote SSE stream to Express response |
| @@ -271,6 +274,7 @@ router.post('/generate', jsonParser, async function (req, res) { | ||
| 271 | 274 | return res.status(response.status).send({ error: { message } }); |
| 272 | 275 | } |
| 273 | 276 | |
| 277 | + /** @type {any} */ | |
| 274 | 278 | const data = await response.json(); |
| 275 | 279 | console.log('NovelAI Output', data?.output); |
| 276 | 280 | return res.send(data); |
| @@ -413,7 +417,6 @@ router.post('/generate-voice', jsonParser, async (request, response) => { | ||
| 413 | 417 | 'Authorization': `Bearer ${token}`, |
| 414 | 418 | 'Accept': 'audio/mpeg', |
| 415 | 419 | }, |
| 416 | - timeout: 0, | |
| 417 | 420 | }); |
| 418 | 421 | |
| 419 | 422 | if (!result.ok) { |
| @@ -423,7 +426,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => { | ||
| 423 | 426 | } |
| 424 | 427 | |
| 425 | 428 | const chunks = await readAllChunks(result.body); |
| 426 | 429 | const buffer = Buffer.concat(chunks.map(chunk => new Uint8Array(chunk))); |
| 427 | 430 | response.setHeader('Content-Type', 'audio/mpeg'); |
| 428 | 431 | return response.send(buffer); |
| 429 | 432 | } |
| @@ -432,5 +435,3 @@ router.post('/generate-voice', jsonParser, async (request, response) => { | ||
| 432 | 435 | return response.sendStatus(500); |
| 433 | 436 | } |
| 434 | 437 | }); |
| 435 | - | |
| 436 | -module.exports = { router }; | |
| @@ -1,14 +1,17 @@ | ||
| 1 | -const { readSecret, SECRET_KEYS } = require('./secrets'); | |
| 1 | +import fs from 'node:fs'; | |
| 2 | -const fetch = require('node-fetch').default; | |
| 2 | +import { Buffer } from 'node:buffer'; | |
| 3 | -const express = require('express'); | |
| 3 | + | |
| 4 | 4 | constimport FormDatafetch =from require('formnode-datafetch'); |
| 5 | -const fs = require('fs'); | |
| 5 | +import FormData from 'form-data'; | |
| 6 | -const { jsonParser, urlencodedParser } = require('../express-common'); | |
| 6 | +import express from 'express'; | |
| 7 | -const { getConfigValue, mergeObjectWithYaml, excludeKeysByYaml, trimV1 } = require('../util'); | |
| 7 | + | |
| 8 | 8 | constimport { setAdditionalHeadersjsonParser, urlencodedParser } =from require('../additionalexpress-headerscommon.js'); |
| 9 | 9 | constimport { OPENROUTER_HEADERSgetConfigValue, mergeObjectWithYaml, excludeKeysByYaml, trimV1 } =from require('../constantsutil.js'); |
| 10 | - | |
| 10 | +import { setAdditionalHeaders } from '../additional-headers.js'; | |
| 11 | -const router = express.Router(); | |
| 11 | +import { readSecret, SECRET_KEYS } from './secrets.js'; | |
| 12 | +import { OPENROUTER_HEADERS } from '../constants.js'; | |
| 13 | + | |
| 14 | +export const router = express.Router(); | |
| 12 | 15 | |
| 13 | 16 | router.post('/caption-image', jsonParser, async (request, response) => { |
| 14 | 17 | try { |
| @@ -151,7 +154,6 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 151 | 154 | ...headers, |
| 152 | 155 | }, |
| 153 | 156 | body: JSON.stringify(body), |
| 154 | - timeout: 0, | |
| 155 | 157 | }); |
| 156 | 158 | |
| 157 | 159 | if (!result.ok) { |
| @@ -160,6 +162,7 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 160 | 162 | return response.status(500).send(text); |
| 161 | 163 | } |
| 162 | 164 | |
| 165 | + /** @type {any} */ | |
| 163 | 166 | const data = await result.json(); |
| 164 | 167 | console.log('Multimodal captioning response', data); |
| 165 | 168 | const caption = data?.choices[0]?.message?.content; |
| @@ -281,7 +284,6 @@ router.post('/generate-image', jsonParser, async (request, response) => { | ||
| 281 | 284 | Authorization: `Bearer ${key}`, |
| 282 | 285 | }, |
| 283 | 286 | body: JSON.stringify(request.body), |
| 284 | - timeout: 0, | |
| 285 | 287 | }); |
| 286 | 288 | |
| 287 | 289 | if (!result.ok) { |
| @@ -341,5 +343,3 @@ custom.post('/generate-voice', jsonParser, async (request, response) => { | ||
| 341 | 343 | }); |
| 342 | 344 | |
| 343 | 345 | router.use('/custom', custom); |
| 344 | - | |
| 345 | -module.exports = { router }; | |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | constimport express =from require('express'); |
| 2 | 2 | constimport { jsonParser } =from require('../express-common.js'); |
| 3 | 3 | |
| 4 | 4 | export const router = express.Router(); |
| 5 | 5 | const API_OPENROUTER = 'https://openrouter.ai/api/v1'; |
| 6 | 6 | |
| 7 | 7 | router.post('/models/multimodal', jsonParser, async (_req, res) => { |
| @@ -28,5 +28,3 @@ router.post('/models/multimodal', jsonParser, async (_req, res) => { | ||
| 28 | 28 | return res.sendStatus(500); |
| 29 | 29 | } |
| 30 | 30 | }); |
| 31 | - | |
| 32 | -module.exports = { router }; | |
| @@ -1,15 +1,17 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 3 | + | |
| 4 | -const sanitize = require('sanitize-filename'); | |
| 4 | +import express from 'express'; | |
| 5 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 5 | +import sanitize from 'sanitize-filename'; | |
| 6 | 6 | constimport { getDefaultPresetFile,sync getDefaultPresetsas writeFileAtomicSync } =from require('./contentwrite-managerfile-atomic'); |
| 7 | -const { jsonParser } = require('../express-common'); | |
| 7 | + | |
| 8 | +import { getDefaultPresetFile, getDefaultPresets } from './content-manager.js'; | |
| 9 | +import { jsonParser } from '../express-common.js'; | |
| 8 | 10 | |
| 9 | 11 | /** |
| 10 | 12 | * Gets the folder and extension for the preset settings based on the API source ID. |
| 11 | 13 | * @param {string} apiId API source ID |
| 12 | 14 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 13 | 15 | * @returns {object} Object containing the folder and extension for the preset settings |
| 14 | 16 | */ |
| 15 | 17 | function getPresetSettingsByAPI(apiId, directories) { |
| @@ -34,7 +36,7 @@ function getPresetSettingsByAPI(apiId, directories) { | ||
| 34 | 36 | } |
| 35 | 37 | } |
| 36 | 38 | |
| 37 | 39 | export const router = express.Router(); |
| 38 | 40 | |
| 39 | 41 | router.post('/save', jsonParser, function (request, response) { |
| 40 | 42 | const name = sanitize(request.body.name); |
| @@ -127,5 +129,3 @@ router.post('/delete-openai', jsonParser, function (request, response) { | ||
| 127 | 129 | |
| 128 | 130 | return response.send({ error: true }); |
| 129 | 131 | }); |
| 130 | - | |
| 131 | -module.exports = { router }; | |
| @@ -1,12 +1,13 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 4 | -const sanitize = require('sanitize-filename'); | |
| 5 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 6 | 3 | |
| 7 | -const { jsonParser } = require('../express-common'); | |
| 4 | +import express from 'express'; | |
| 5 | +import sanitize from 'sanitize-filename'; | |
| 6 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 8 | 7 | |
| 9 | -const router = express.Router(); | |
| 8 | +import { jsonParser } from '../express-common.js'; | |
| 9 | + | |
| 10 | +export const router = express.Router(); | |
| 10 | 11 | |
| 11 | 12 | router.post('/save', jsonParser, (request, response) => { |
| 12 | 13 | if (!request.body || !request.body.name) { |
| @@ -31,5 +32,3 @@ router.post('/delete', jsonParser, (request, response) => { | ||
| 31 | 32 | |
| 32 | 33 | return response.sendStatus(200); |
| 33 | 34 | }); |
| 34 | - | |
| 35 | -module.exports = { router }; | |
| @@ -1,9 +1,11 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport express =from require('express'); |
| 3 | -const { readSecret, SECRET_KEYS } = require('./secrets'); | |
| 4 | -const { jsonParser } = require('../express-common'); | |
| 5 | 3 | |
| 6 | -const router = express.Router(); | |
| 4 | +import { decode } from 'html-entities'; | |
| 5 | +import { readSecret, SECRET_KEYS } from './secrets.js'; | |
| 6 | +import { jsonParser } from '../express-common.js'; | |
| 7 | + | |
| 8 | +export const router = express.Router(); | |
| 7 | 9 | |
| 8 | 10 | // Cosplay as Chrome |
| 9 | 11 | const visitHeaders = { |
| @@ -29,7 +31,6 @@ const visitHeaders = { | ||
| 29 | 31 | * @returns {Promise<string>} Transcript text |
| 30 | 32 | */ |
| 31 | 33 | async function extractTranscript(videoPageBody, lang) { |
| 32 | - const he = require('he'); | |
| 33 | 34 | const RE_XML_TRANSCRIPT = /<text start="([^"]*)" dur="([^"]*)">([^<]*)<\/text>/g; |
| 34 | 35 | const splittedHTML = videoPageBody.split('"captions":'); |
| 35 | 36 | |
| @@ -84,7 +85,7 @@ async function extractTranscript(videoPageBody, lang) { | ||
| 84 | 85 | lang: lang ?? captions.captionTracks[0].languageCode, |
| 85 | 86 | })); |
| 86 | 87 | // The text is double-encoded |
| 87 | 88 | const transcriptText = transcript.map((line) => he.decode(he.decode(line.text))).join(' '); |
| 88 | 89 | return transcriptText; |
| 89 | 90 | } |
| 90 | 91 | |
| @@ -263,5 +264,3 @@ router.post('/visit', jsonParser, async (request, response) => { | ||
| 263 | 264 | return response.sendStatus(500); |
| 264 | 265 | } |
| 265 | 266 | }); |
| 266 | - | |
| 267 | -module.exports = { router }; | |
| @@ -1,12 +1,13 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 3 | + | |
| 4 | -const { getConfigValue } = require('../util'); | |
| 4 | +import express from 'express'; | |
| 5 | 5 | constimport { sync as writeFileAtomicSync =} require(from 'write-file-atomic').sync; |
| 6 | 6 | constimport { jsonParsergetConfigValue } =from require('../express-commonutil.js'); |
| 7 | - | |
| 7 | +import { jsonParser } from '../express-common.js'; | |
| 8 | -const SECRETS_FILE = 'secrets.json'; | |
| 8 | + | |
| 9 | -const SECRET_KEYS = { | |
| 9 | +export const SECRETS_FILE = 'secrets.json'; | |
| 10 | +export const SECRET_KEYS = { | |
| 10 | 11 | HORDE: 'api_key_horde', |
| 11 | 12 | MANCER: 'api_key_mancer', |
| 12 | 13 | VLLM: 'api_key_vllm', |
| @@ -58,11 +59,11 @@ const EXPORTABLE_KEYS = [ | ||
| 58 | 59 | |
| 59 | 60 | /** |
| 60 | 61 | * Writes a secret to the secrets file |
| 61 | 62 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 62 | 63 | * @param {string} key Secret key |
| 63 | 64 | * @param {string} value Secret value |
| 64 | 65 | */ |
| 65 | 66 | export function writeSecret(directories, key, value) { |
| 66 | 67 | const filePath = path.join(directories.root, SECRETS_FILE); |
| 67 | 68 | |
| 68 | 69 | if (!fs.existsSync(filePath)) { |
| @@ -78,11 +79,11 @@ function writeSecret(directories, key, value) { | ||
| 78 | 79 | |
| 79 | 80 | /** |
| 80 | 81 | * Deletes a secret from the secrets file |
| 81 | 82 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 82 | 83 | * @param {string} key Secret key |
| 83 | 84 | * @returns |
| 84 | 85 | */ |
| 85 | 86 | export function deleteSecret(directories, key) { |
| 86 | 87 | const filePath = path.join(directories.root, SECRETS_FILE); |
| 87 | 88 | |
| 88 | 89 | if (!fs.existsSync(filePath)) { |
| @@ -97,11 +98,11 @@ function deleteSecret(directories, key) { | ||
| 97 | 98 | |
| 98 | 99 | /** |
| 99 | 100 | * Reads a secret from the secrets file |
| 100 | 101 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 101 | 102 | * @param {string} key Secret key |
| 102 | 103 | * @returns {string} Secret value |
| 103 | 104 | */ |
| 104 | 105 | export function readSecret(directories, key) { |
| 105 | 106 | const filePath = path.join(directories.root, SECRETS_FILE); |
| 106 | 107 | |
| 107 | 108 | if (!fs.existsSync(filePath)) { |
| @@ -115,10 +116,10 @@ function readSecret(directories, key) { | ||
| 115 | 116 | |
| 116 | 117 | /** |
| 117 | 118 | * Reads the secret state from the secrets file |
| 118 | 119 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 119 | 120 | * @returns {object} Secret state |
| 120 | 121 | */ |
| 121 | 122 | export function readSecretState(directories) { |
| 122 | 123 | const filePath = path.join(directories.root, SECRETS_FILE); |
| 123 | 124 | |
| 124 | 125 | if (!fs.existsSync(filePath)) { |
| @@ -138,10 +139,10 @@ function readSecretState(directories) { | ||
| 138 | 139 | |
| 139 | 140 | /** |
| 140 | 141 | * Reads all secrets from the secrets file |
| 141 | 142 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 142 | 143 | * @returns {Record<string, string> | undefined} Secrets |
| 143 | 144 | */ |
| 144 | 145 | export function getAllSecrets(directories) { |
| 145 | 146 | const filePath = path.join(directories.root, SECRETS_FILE); |
| 146 | 147 | |
| 147 | 148 | if (!fs.existsSync(filePath)) { |
| @@ -154,7 +155,7 @@ function getAllSecrets(directories) { | ||
| 154 | 155 | return secrets; |
| 155 | 156 | } |
| 156 | 157 | |
| 157 | 158 | export const router = express.Router(); |
| 158 | 159 | |
| 159 | 160 | router.post('/write', jsonParser, (request, response) => { |
| 160 | 161 | const key = request.body.key; |
| @@ -218,13 +219,3 @@ router.post('/find', jsonParser, (request, response) => { | ||
| 218 | 219 | return response.sendStatus(500); |
| 219 | 220 | } |
| 220 | 221 | }); |
| 221 | - | |
| 222 | -module.exports = { | |
| 223 | - writeSecret, | |
| 224 | - readSecret, | |
| 225 | - deleteSecret, | |
| 226 | - readSecretState, | |
| 227 | - getAllSecrets, | |
| 228 | - SECRET_KEYS, | |
| 229 | - router, | |
| 230 | -}; | |
| @@ -1,12 +1,14 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 3 | + | |
| 4 | 4 | constimport _express =from require('lodashexpress'); |
| 5 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 5 | +import _ from 'lodash'; | |
| 6 | -const { SETTINGS_FILE } = require('../constants'); | |
| 6 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 7 | -const { getConfigValue, generateTimestamp, removeOldBackups } = require('../util'); | |
| 7 | + | |
| 8 | 8 | constimport { jsonParserSETTINGS_FILE } =from require('../express-commonconstants.js'); |
| 9 | 9 | constimport { getAllUserHandlesgetConfigValue, getUserDirectoriesgenerateTimestamp, removeOldBackups } =from require('../usersutil.js'); |
| 10 | +import { jsonParser } from '../express-common.js'; | |
| 11 | +import { getAllUserHandles, getUserDirectories } from '../users.js'; | |
| 10 | 12 | |
| 11 | 13 | const ENABLE_EXTENSIONS = getConfigValue('enableExtensions', true); |
| 12 | 14 | const ENABLE_EXTENSIONS_AUTO_UPDATE = getConfigValue('enableExtensionsAutoUpdate', true); |
| @@ -190,7 +192,7 @@ function getLatestBackup(handle) { | ||
| 190 | 192 | return path.join(userDirectories.backups, latestBackup); |
| 191 | 193 | } |
| 192 | 194 | |
| 193 | 195 | export const router = express.Router(); |
| 194 | 196 | |
| 195 | 197 | router.post('/save', jsonParser, function (request, response) { |
| 196 | 198 | try { |
| @@ -357,8 +359,6 @@ router.post('/restore-snapshot', jsonParser, async (request, response) => { | ||
| 357 | 359 | /** |
| 358 | 360 | * Initializes the settings endpoint |
| 359 | 361 | */ |
| 360 | 362 | export async function init() { |
| 361 | 363 | await backupSettings(); |
| 362 | 364 | } |
| 363 | - | |
| 364 | -module.exports = { router, init }; | |
| @@ -1,7 +1,10 @@ | ||
| 1 | -const express = require('express'); | |
| 1 | +import { Buffer } from 'node:buffer'; | |
| 2 | -const { jsonParser } = require('../express-common'); | |
| 2 | +import express from 'express'; | |
| 3 | +import wavefile from 'wavefile'; | |
| 4 | +import { jsonParser } from '../express-common.js'; | |
| 5 | +import { getPipeline } from '../transformers.mjs'; | |
| 3 | 6 | |
| 4 | 7 | export const router = express.Router(); |
| 5 | 8 | |
| 6 | 9 | /** |
| 7 | 10 | * Gets the audio data from a base64-encoded audio file. |
| @@ -9,7 +12,6 @@ const router = express.Router(); | ||
| 9 | 12 | * @returns {Float64Array} Audio data |
| 10 | 13 | */ |
| 11 | 14 | function getWaveFile(audio) { |
| 12 | - const wavefile = require('wavefile'); | |
| 13 | 15 | const wav = new wavefile.WaveFile(); |
| 14 | 16 | wav.fromDataURI(audio); |
| 15 | 17 | wav.toBitDepth('32f'); |
| @@ -36,8 +38,7 @@ router.post('/recognize', jsonParser, async (req, res) => { | ||
| 36 | 38 | try { |
| 37 | 39 | const TASK = 'automatic-speech-recognition'; |
| 38 | 40 | const { model, audio, lang } = req.body; |
| 39 | 41 | const modulepipe = await importgetPipeline('../transformers.mjs'TASK, model); |
| 40 | - const pipe = await module.default.getPipeline(TASK, model); | |
| 41 | 42 | const wav = getWaveFile(audio); |
| 42 | 43 | const start = performance.now(); |
| 43 | 44 | const result = await pipe(wav, { language: lang || null, task: 'transcribe' }); |
| @@ -54,11 +55,9 @@ router.post('/recognize', jsonParser, async (req, res) => { | ||
| 54 | 55 | |
| 55 | 56 | router.post('/synthesize', jsonParser, async (req, res) => { |
| 56 | 57 | try { |
| 57 | - const wavefile = require('wavefile'); | |
| 58 | 58 | const TASK = 'text-to-speech'; |
| 59 | 59 | const { text, model, speaker } = req.body; |
| 60 | 60 | const modulepipe = await importgetPipeline('../transformers.mjs'TASK, model); |
| 61 | - const pipe = await module.default.getPipeline(TASK, model); | |
| 62 | 61 | const speaker_embeddings = speaker |
| 63 | 62 | ? new Float32Array(new Uint8Array(Buffer.from(speaker.startsWith('data:') ? speaker.split(',')[1] : speaker, 'base64')).buffer) |
| 64 | 63 | : null; |
| @@ -78,5 +77,3 @@ router.post('/synthesize', jsonParser, async (req, res) => { | ||
| 78 | 77 | return res.sendStatus(500); |
| 79 | 78 | } |
| 80 | 79 | }); |
| 81 | - | |
| 82 | -module.exports = { router }; | |
| @@ -1,16 +1,17 @@ | ||
| 1 | +import fs from 'node:fs'; | |
| 2 | +import path from 'node:path'; | |
| 1 | 3 | |
| 2 | 4 | constimport fsexpress =from require('fsexpress'); |
| 3 | -const path = require('path'); | |
| 5 | +import mime from 'mime-types'; | |
| 4 | -const express = require('express'); | |
| 6 | +import sanitize from 'sanitize-filename'; | |
| 5 | -const mime = require('mime-types'); | |
| 7 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 6 | -const sanitize = require('sanitize-filename'); | |
| 8 | + | |
| 7 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 9 | +import { getImageBuffers } from '../util.js'; | |
| 8 | 10 | constimport { getImageBuffersjsonParser, urlencodedParser } =from require('../utilexpress-common.js'); |
| 9 | -const { jsonParser, urlencodedParser } = require('../express-common'); | |
| 10 | 11 | |
| 11 | 12 | /** |
| 12 | 13 | * Gets the path to the sprites folder for the provided character name |
| 13 | 14 | * @param {import('../users.js').UserDirectoryList} directories - User directories |
| 14 | 15 | * @param {string} name - The name of the character |
| 15 | 16 | * @param {boolean} isSubfolder - Whether the name contains a subfolder |
| 16 | 17 | * @returns {string | null} The path to the sprites folder. Null if the name is invalid. |
| @@ -41,11 +42,11 @@ function getSpritesPath(directories, name, isSubfolder) { | ||
| 41 | 42 | * Imports base64 encoded sprites from RisuAI character data. |
| 42 | 43 | * The sprites are saved in the character's sprites folder. |
| 43 | 44 | * The additionalAssets and emotions are removed from the data. |
| 44 | 45 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 45 | 46 | * @param {object} data RisuAI character data |
| 46 | 47 | * @returns {void} |
| 47 | 48 | */ |
| 48 | 49 | export function importRisuSprites(directories, data) { |
| 49 | 50 | try { |
| 50 | 51 | const name = data?.data?.name; |
| 51 | 52 | const risuData = data?.data?.extensions?.risuai; |
| @@ -106,7 +107,7 @@ function importRisuSprites(directories, data) { | ||
| 106 | 107 | } |
| 107 | 108 | } |
| 108 | 109 | |
| 109 | 110 | export const router = express.Router(); |
| 110 | 111 | |
| 111 | 112 | router.get('/get', jsonParser, function (request, response) { |
| 112 | 113 | const name = String(request.query.name); |
| @@ -259,8 +260,3 @@ router.post('/upload', urlencodedParser, async (request, response) => { | ||
| 259 | 260 | return response.sendStatus(500); |
| 260 | 261 | } |
| 261 | 262 | }); |
| 262 | - | |
| 263 | -module.exports = { | |
| 264 | - router, | |
| 265 | - importRisuSprites, | |
| 266 | -}; | |
| @@ -1,13 +1,15 @@ | ||
| 1 | -const express = require('express'); | |
| 1 | +import fs from 'node:fs'; | |
| 2 | -const fetch = require('node-fetch').default; | |
| 2 | +import path from 'node:path'; | |
| 3 | -const sanitize = require('sanitize-filename'); | |
| 3 | + | |
| 4 | -const { getBasicAuthHeader, delay, getHexString } = require('../util.js'); | |
| 4 | +import express from 'express'; | |
| 5 | -const fs = require('fs'); | |
| 5 | +import fetch from 'node-fetch'; | |
| 6 | -const path = require('path'); | |
| 6 | +import sanitize from 'sanitize-filename'; | |
| 7 | 7 | constimport { sync as writeFileAtomicSync =} require(from 'write-file-atomic').sync; |
| 8 | -const { jsonParser } = require('../express-common'); | |
| 8 | +import FormData from 'form-data'; | |
| 9 | -const { readSecret, SECRET_KEYS } = require('./secrets.js'); | |
| 9 | + | |
| 10 | -const FormData = require('form-data'); | |
| 10 | +import { getBasicAuthHeader, delay } from '../util.js'; | |
| 11 | +import { jsonParser } from '../express-common.js'; | |
| 12 | +import { readSecret, SECRET_KEYS } from './secrets.js'; | |
| 11 | 13 | |
| 12 | 14 | /** |
| 13 | 15 | * Gets the comfy workflows. |
| @@ -21,7 +23,7 @@ function getComfyWorkflows(directories) { | ||
| 21 | 23 | .sort(Intl.Collator().compare); |
| 22 | 24 | } |
| 23 | 25 | |
| 24 | 26 | export const router = express.Router(); |
| 25 | 27 | |
| 26 | 28 | router.post('/ping', jsonParser, async (request, response) => { |
| 27 | 29 | try { |
| @@ -63,6 +65,7 @@ router.post('/upscalers', jsonParser, async (request, response) => { | ||
| 63 | 65 | throw new Error('SD WebUI returned an error.'); |
| 64 | 66 | } |
| 65 | 67 | |
| 68 | + /** @type {any} */ | |
| 66 | 69 | const data = await result.json(); |
| 67 | 70 | const names = data.map(x => x.name); |
| 68 | 71 | return names; |
| @@ -83,6 +86,7 @@ router.post('/upscalers', jsonParser, async (request, response) => { | ||
| 83 | 86 | throw new Error('SD WebUI returned an error.'); |
| 84 | 87 | } |
| 85 | 88 | |
| 89 | + /** @type {any} */ | |
| 86 | 90 | const data = await result.json(); |
| 87 | 91 | const names = data.map(x => x.name); |
| 88 | 92 | return names; |
| @@ -116,6 +120,7 @@ router.post('/vaes', jsonParser, async (request, response) => { | ||
| 116 | 120 | throw new Error('SD WebUI returned an error.'); |
| 117 | 121 | } |
| 118 | 122 | |
| 123 | + /** @type {any} */ | |
| 119 | 124 | const data = await result.json(); |
| 120 | 125 | const names = data.map(x => x.model_name); |
| 121 | 126 | return response.send(names); |
| @@ -141,6 +146,7 @@ router.post('/samplers', jsonParser, async (request, response) => { | ||
| 141 | 146 | throw new Error('SD WebUI returned an error.'); |
| 142 | 147 | } |
| 143 | 148 | |
| 149 | + /** @type {any} */ | |
| 144 | 150 | const data = await result.json(); |
| 145 | 151 | const names = data.map(x => x.name); |
| 146 | 152 | return response.send(names); |
| @@ -167,6 +173,7 @@ router.post('/schedulers', jsonParser, async (request, response) => { | ||
| 167 | 173 | throw new Error('SD WebUI returned an error.'); |
| 168 | 174 | } |
| 169 | 175 | |
| 176 | + /** @type {any} */ | |
| 170 | 177 | const data = await result.json(); |
| 171 | 178 | const names = data.map(x => x.name); |
| 172 | 179 | return response.send(names); |
| @@ -192,6 +199,7 @@ router.post('/models', jsonParser, async (request, response) => { | ||
| 192 | 199 | throw new Error('SD WebUI returned an error.'); |
| 193 | 200 | } |
| 194 | 201 | |
| 202 | + /** @type {any} */ | |
| 195 | 203 | const data = await result.json(); |
| 196 | 204 | const models = data.map(x => ({ value: x.title, text: x.title })); |
| 197 | 205 | return response.send(models); |
| @@ -212,6 +220,7 @@ router.post('/get-model', jsonParser, async (request, response) => { | ||
| 212 | 220 | 'Authorization': getBasicAuthHeader(request.body.auth), |
| 213 | 221 | }, |
| 214 | 222 | }); |
| 223 | + /** @type {any} */ | |
| 215 | 224 | const data = await result.json(); |
| 216 | 225 | return response.send(data['sd_model_checkpoint']); |
| 217 | 226 | } catch (error) { |
| @@ -231,7 +240,6 @@ router.post('/set-model', jsonParser, async (request, response) => { | ||
| 231 | 240 | headers: { |
| 232 | 241 | 'Authorization': getBasicAuthHeader(request.body.auth), |
| 233 | 242 | }, |
| 234 | - timeout: 0, | |
| 235 | 243 | }); |
| 236 | 244 | const data = await result.json(); |
| 237 | 245 | return data; |
| @@ -251,7 +259,6 @@ router.post('/set-model', jsonParser, async (request, response) => { | ||
| 251 | 259 | 'Content-Type': 'application/json', |
| 252 | 260 | 'Authorization': getBasicAuthHeader(request.body.auth), |
| 253 | 261 | }, |
| 254 | - timeout: 0, | |
| 255 | 262 | }); |
| 256 | 263 | |
| 257 | 264 | if (!result.ok) { |
| @@ -262,6 +269,7 @@ router.post('/set-model', jsonParser, async (request, response) => { | ||
| 262 | 269 | const CHECK_INTERVAL = 2000; |
| 263 | 270 | |
| 264 | 271 | for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { |
| 272 | + /** @type {any} */ | |
| 265 | 273 | const progressState = await getProgress(); |
| 266 | 274 | |
| 267 | 275 | const progress = progressState['progress']; |
| @@ -306,8 +314,6 @@ router.post('/generate', jsonParser, async (request, response) => { | ||
| 306 | 314 | 'Content-Type': 'application/json', |
| 307 | 315 | 'Authorization': getBasicAuthHeader(request.body.auth), |
| 308 | 316 | }, |
| 309 | - timeout: 0, | |
| 310 | - // @ts-ignore | |
| 311 | 317 | signal: controller.signal, |
| 312 | 318 | }); |
| 313 | 319 | |
| @@ -343,6 +349,7 @@ router.post('/sd-next/upscalers', jsonParser, async (request, response) => { | ||
| 343 | 349 | // Vlad doesn't provide Latent Upscalers in the API, so we have to hardcode them here |
| 344 | 350 | const latentUpscalers = ['Latent', 'Latent (antialiased)', 'Latent (bicubic)', 'Latent (bicubic antialiased)', 'Latent (nearest)', 'Latent (nearest-exact)']; |
| 345 | 351 | |
| 352 | + /** @type {any} */ | |
| 346 | 353 | const data = await result.json(); |
| 347 | 354 | const names = data.map(x => x.name); |
| 348 | 355 | |
| @@ -385,6 +392,7 @@ comfy.post('/samplers', jsonParser, async (request, response) => { | ||
| 385 | 392 | throw new Error('ComfyUI returned an error.'); |
| 386 | 393 | } |
| 387 | 394 | |
| 395 | + /** @type {any} */ | |
| 388 | 396 | const data = await result.json(); |
| 389 | 397 | return response.send(data.KSampler.input.required.sampler_name[0]); |
| 390 | 398 | } catch (error) { |
| @@ -402,6 +410,7 @@ comfy.post('/models', jsonParser, async (request, response) => { | ||
| 402 | 410 | if (!result.ok) { |
| 403 | 411 | throw new Error('ComfyUI returned an error.'); |
| 404 | 412 | } |
| 413 | + /** @type {any} */ | |
| 405 | 414 | const data = await result.json(); |
| 406 | 415 | return response.send(data.CheckpointLoaderSimple.input.required.ckpt_name[0].map(it => ({ value: it, text: it }))); |
| 407 | 416 | } catch (error) { |
| @@ -420,6 +429,7 @@ comfy.post('/schedulers', jsonParser, async (request, response) => { | ||
| 420 | 429 | throw new Error('ComfyUI returned an error.'); |
| 421 | 430 | } |
| 422 | 431 | |
| 432 | + /** @type {any} */ | |
| 423 | 433 | const data = await result.json(); |
| 424 | 434 | return response.send(data.KSampler.input.required.scheduler[0]); |
| 425 | 435 | } catch (error) { |
| @@ -438,6 +448,7 @@ comfy.post('/vaes', jsonParser, async (request, response) => { | ||
| 438 | 448 | throw new Error('ComfyUI returned an error.'); |
| 439 | 449 | } |
| 440 | 450 | |
| 451 | + /** @type {any} */ | |
| 441 | 452 | const data = await result.json(); |
| 442 | 453 | return response.send(data.VAELoader.input.required.vae_name[0]); |
| 443 | 454 | } catch (error) { |
| @@ -519,6 +530,7 @@ comfy.post('/generate', jsonParser, async (request, response) => { | ||
| 519 | 530 | throw new Error('ComfyUI returned an error.'); |
| 520 | 531 | } |
| 521 | 532 | |
| 533 | + /** @type {any} */ | |
| 522 | 534 | const data = await promptResult.json(); |
| 523 | 535 | const id = data.prompt_id; |
| 524 | 536 | let item; |
| @@ -529,6 +541,7 @@ comfy.post('/generate', jsonParser, async (request, response) => { | ||
| 529 | 541 | if (!result.ok) { |
| 530 | 542 | throw new Error('ComfyUI returned an error.'); |
| 531 | 543 | } |
| 544 | + /** @type {any} */ | |
| 532 | 545 | const history = await result.json(); |
| 533 | 546 | item = history[id]; |
| 534 | 547 | if (item) { |
| @@ -631,6 +644,7 @@ together.post('/generate', jsonParser, async (request, response) => { | ||
| 631 | 644 | return response.sendStatus(500); |
| 632 | 645 | } |
| 633 | 646 | |
| 647 | + /** @type {any} */ | |
| 634 | 648 | const data = await result.json(); |
| 635 | 649 | console.log('TogetherAI response:', data); |
| 636 | 650 | |
| @@ -679,6 +693,8 @@ drawthings.post('/get-model', jsonParser, async (request, response) => { | ||
| 679 | 693 | const result = await fetch(url, { |
| 680 | 694 | method: 'GET', |
| 681 | 695 | }); |
| 696 | + | |
| 697 | + /** @type {any} */ | |
| 682 | 698 | const data = await result.json(); |
| 683 | 699 | |
| 684 | 700 | return response.send(data['model']); |
| @@ -696,6 +712,8 @@ drawthings.post('/get-upscaler', jsonParser, async (request, response) => { | ||
| 696 | 712 | const result = await fetch(url, { |
| 697 | 713 | method: 'GET', |
| 698 | 714 | }); |
| 715 | + | |
| 716 | + /** @type {any} */ | |
| 699 | 717 | const data = await result.json(); |
| 700 | 718 | |
| 701 | 719 | return response.send(data['upscaler']); |
| @@ -724,7 +742,6 @@ drawthings.post('/generate', jsonParser, async (request, response) => { | ||
| 724 | 742 | 'Content-Type': 'application/json', |
| 725 | 743 | 'Authorization': auth, |
| 726 | 744 | }, |
| 727 | - timeout: 0, | |
| 728 | 745 | }); |
| 729 | 746 | |
| 730 | 747 | if (!result.ok) { |
| @@ -846,7 +863,6 @@ stability.post('/generate', jsonParser, async (request, response) => { | ||
| 846 | 863 | 'Accept': 'image/*', |
| 847 | 864 | }, |
| 848 | 865 | body: formData, |
| 849 | - timeout: 0, | |
| 850 | 866 | }); |
| 851 | 867 | |
| 852 | 868 | if (!result.ok) { |
| @@ -993,5 +1009,3 @@ router.use('/pollinations', pollinations); | ||
| 993 | 1009 | router.use('/stability', stability); |
| 994 | 1010 | router.use('/blockentropy', blockentropy); |
| 995 | 1011 | router.use('/huggingface', huggingface); |
| 996 | - | |
| 997 | -module.exports = { router }; | |
| @@ -1,14 +1,15 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 3 | +import crypto from 'node:crypto'; | |
| 4 | -const writeFileAtomic = require('write-file-atomic'); | |
| 4 | + | |
| 5 | 5 | constimport cryptoexpress =from require('cryptoexpress'); |
| 6 | +import writeFileAtomic from 'write-file-atomic'; | |
| 6 | 7 | |
| 7 | 8 | const readFile = fs.promises.readFile; |
| 8 | 9 | const readdir = fs.promises.readdir; |
| 9 | 10 | |
| 10 | 11 | constimport { jsonParser } =from require('../express-common.js'); |
| 11 | 12 | constimport { getAllUserHandles, getUserDirectories } =from require('../users.js'); |
| 12 | 13 | |
| 13 | 14 | const STATS_FILE = 'stats.json'; |
| 14 | 15 | |
| @@ -146,7 +147,7 @@ async function collectAndCreateStats(chatsPath, charactersPath) { | ||
| 146 | 147 | * @param {string} chatsPath Path to the directory containing the chat files. |
| 147 | 148 | * @param {string} charactersPath Path to the directory containing the character files. |
| 148 | 149 | */ |
| 149 | 150 | export async function recreateStats(handle, chatsPath, charactersPath) { |
| 150 | 151 | console.log('Collecting and creating stats for user:', handle); |
| 151 | 152 | const stats = await collectAndCreateStats(chatsPath, charactersPath); |
| 152 | 153 | STATS.set(handle, stats); |
| @@ -157,7 +158,7 @@ async function recreateStats(handle, chatsPath, charactersPath) { | ||
| 157 | 158 | * Loads the stats file into memory. If the file doesn't exist or is invalid, |
| 158 | 159 | * initializes stats by collecting and creating them for each character. |
| 159 | 160 | */ |
| 160 | 161 | export async function init() { |
| 161 | 162 | try { |
| 162 | 163 | const userHandles = await getAllUserHandles(); |
| 163 | 164 | for (const handle of userHandles) { |
| @@ -209,7 +210,7 @@ async function saveStatsToFile() { | ||
| 209 | 210 | * Attempts to save charStats to a file and then terminates the process. |
| 210 | 211 | * If an error occurs during the file write, it logs the error before exiting. |
| 211 | 212 | */ |
| 212 | 213 | export async function onExit() { |
| 213 | 214 | try { |
| 214 | 215 | await saveStatsToFile(); |
| 215 | 216 | } catch (err) { |
| @@ -434,7 +435,7 @@ function calculateTotalGenTimeAndWordCount( | ||
| 434 | 435 | }; |
| 435 | 436 | } |
| 436 | 437 | |
| 437 | 438 | export const router = express.Router(); |
| 438 | 439 | |
| 439 | 440 | /** |
| 440 | 441 | * Handle a POST request to get the stats object |
| @@ -465,10 +466,3 @@ router.post('/update', jsonParser, function (request, response) { | ||
| 465 | 466 | setCharStats(request.user.profile.handle, request.body); |
| 466 | 467 | return response.sendStatus(200); |
| 467 | 468 | }); |
| 468 | - | |
| 469 | -module.exports = { | |
| 470 | - router, | |
| 471 | - recreateStats, | |
| 472 | - init, | |
| 473 | - onExit, | |
| 474 | -}; | |
| @@ -1,11 +1,13 @@ | ||
| 1 | -const express = require('express'); | |
| 1 | +import path from 'node:path'; | |
| 2 | -const path = require('path'); | |
| 2 | +import fs from 'node:fs'; | |
| 3 | -const fs = require('fs'); | |
| 4 | -const sanitize = require('sanitize-filename'); | |
| 5 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 6 | -const { jsonParser } = require('../express-common'); | |
| 7 | 3 | |
| 8 | -const router = express.Router(); | |
| 4 | +import express from 'express'; | |
| 5 | +import sanitize from 'sanitize-filename'; | |
| 6 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 7 | + | |
| 8 | +import { jsonParser } from '../express-common.js'; | |
| 9 | + | |
| 10 | +export const router = express.Router(); | |
| 9 | 11 | |
| 10 | 12 | router.post('/save', jsonParser, (request, response) => { |
| 11 | 13 | if (!request.body || !request.body.name) { |
| @@ -36,5 +38,3 @@ router.post('/delete', jsonParser, function (request, response) { | ||
| 36 | 38 | return response.sendStatus(500); |
| 37 | 39 | } |
| 38 | 40 | }); |
| 39 | - | |
| 40 | -module.exports = { router }; | |
| @@ -1,14 +1,16 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | -const fsPromises = require('fs').promises; | |
| 2 | +import { promises as fsPromises } from 'node:fs'; | |
| 3 | 3 | constimport path =from require('node:path'); |
| 4 | -const mime = require('mime-types'); | |
| 4 | + | |
| 5 | -const express = require('express'); | |
| 5 | +import mime from 'mime-types'; | |
| 6 | -const sanitize = require('sanitize-filename'); | |
| 6 | +import express from 'express'; | |
| 7 | -const jimp = require('jimp'); | |
| 7 | +import sanitize from 'sanitize-filename'; | |
| 8 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 8 | +import jimp from 'jimp'; | |
| 9 | -const { getAllUserHandles, getUserDirectories } = require('../users'); | |
| 9 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 10 | -const { getConfigValue } = require('../util'); | |
| 10 | + | |
| 11 | 11 | constimport { jsonParsergetAllUserHandles, getUserDirectories } =from require('../express-commonusers.js'); |
| 12 | +import { getConfigValue } from '../util.js'; | |
| 13 | +import { jsonParser } from '../express-common.js'; | |
| 12 | 14 | |
| 13 | 15 | const thumbnailsDisabled = getConfigValue('disableThumbnails', false); |
| 14 | 16 | const quality = getConfigValue('thumbnailsQuality', 95); |
| @@ -16,7 +18,7 @@ const pngFormat = getConfigValue('avatarThumbnailsPng', false); | ||
| 16 | 18 | |
| 17 | 19 | /** |
| 18 | 20 | * Gets a path to thumbnail folder based on the type. |
| 19 | 21 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 20 | 22 | * @param {'bg' | 'avatar'} type Thumbnail type |
| 21 | 23 | * @returns {string} Path to the thumbnails folder |
| 22 | 24 | */ |
| @@ -37,7 +39,7 @@ function getThumbnailFolder(directories, type) { | ||
| 37 | 39 | |
| 38 | 40 | /** |
| 39 | 41 | * Gets a path to the original images folder based on the type. |
| 40 | 42 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 41 | 43 | * @param {'bg' | 'avatar'} type Thumbnail type |
| 42 | 44 | * @returns {string} Path to the original images folder |
| 43 | 45 | */ |
| @@ -58,11 +60,11 @@ function getOriginalFolder(directories, type) { | ||
| 58 | 60 | |
| 59 | 61 | /** |
| 60 | 62 | * Removes the generated thumbnail from the disk. |
| 61 | 63 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 62 | 64 | * @param {'bg' | 'avatar'} type Type of the thumbnail |
| 63 | 65 | * @param {string} file Name of the file |
| 64 | 66 | */ |
| 65 | 67 | export function invalidateThumbnail(directories, type, file) { |
| 66 | 68 | const folder = getThumbnailFolder(directories, type); |
| 67 | 69 | if (folder === undefined) throw new Error('Invalid thumbnail type'); |
| 68 | 70 | |
| @@ -75,7 +77,7 @@ function invalidateThumbnail(directories, type, file) { | ||
| 75 | 77 | |
| 76 | 78 | /** |
| 77 | 79 | * Generates a thumbnail for the given file. |
| 78 | 80 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 79 | 81 | * @param {'bg' | 'avatar'} type Type of the thumbnail |
| 80 | 82 | * @param {string} file Name of the file |
| 81 | 83 | * @returns |
| @@ -141,7 +143,7 @@ async function generateThumbnail(directories, type, file) { | ||
| 141 | 143 | * Ensures that the thumbnail cache for backgrounds is valid. |
| 142 | 144 | * @returns {Promise<void>} Promise that resolves when the cache is validated |
| 143 | 145 | */ |
| 144 | 146 | export async function ensureThumbnailCache() { |
| 145 | 147 | const userHandles = await getAllUserHandles(); |
| 146 | 148 | for (const handle of userHandles) { |
| 147 | 149 | const directories = getUserDirectories(handle); |
| @@ -166,7 +168,7 @@ async function ensureThumbnailCache() { | ||
| 166 | 168 | } |
| 167 | 169 | } |
| 168 | 170 | |
| 169 | 171 | export const router = express.Router(); |
| 170 | 172 | |
| 171 | 173 | // Important: This route must be mounted as '/thumbnail'. It is used in the client code and saved to chat files. |
| 172 | 174 | router.get('/', jsonParser, async function (request, response) { |
| @@ -227,9 +229,3 @@ router.get('/', jsonParser, async function (request, response) { | ||
| 227 | 229 | return response.sendStatus(500); |
| 228 | 230 | } |
| 229 | 231 | }); |
| 230 | - | |
| 231 | -module.exports = { | |
| 232 | - invalidateThumbnail, | |
| 233 | - ensureThumbnailCache, | |
| 234 | - router, | |
| 235 | -}; | |
| @@ -1,15 +1,19 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 3 | +import { Buffer } from 'node:buffer'; | |
| 4 | -const { SentencePieceProcessor } = require('@agnai/sentencepiece-js'); | |
| 4 | + | |
| 5 | 5 | constimport tiktokenexpress =from require('tiktokenexpress'); |
| 6 | 6 | constimport { Tokenizersync as writeFileAtomicSync } =from require('@agnai/webwrite-tokenizersfile-atomic'); |
| 7 | -const { convertClaudePrompt } = require('../prompt-converters'); | |
| 7 | + | |
| 8 | 8 | constimport { TEXTGEN_TYPESTokenizer } =from require('..@agnai/constantsweb-tokenizers'); |
| 9 | 9 | constimport { jsonParserSentencePieceProcessor } =from require('..@agnai/expresssentencepiece-commonjs'); |
| 10 | -const { setAdditionalHeaders } = require('../additional-headers'); | |
| 10 | +import tiktoken from 'tiktoken'; | |
| 11 | -const { getConfigValue, isValidUrl } = require('../util'); | |
| 11 | + | |
| 12 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 12 | +import { convertClaudePrompt } from '../prompt-converters.js'; | |
| 13 | +import { TEXTGEN_TYPES } from '../constants.js'; | |
| 14 | +import { jsonParser } from '../express-common.js'; | |
| 15 | +import { setAdditionalHeaders } from '../additional-headers.js'; | |
| 16 | +import { getConfigValue, isValidUrl } from '../util.js'; | |
| 13 | 17 | |
| 14 | 18 | /** |
| 15 | 19 | * @typedef { (req: import('express').Request, res: import('express').Response) => Promise<any> } TokenizationHandler |
| @@ -23,7 +27,7 @@ const tokenizersCache = {}; | ||
| 23 | 27 | /** |
| 24 | 28 | * @type {string[]} |
| 25 | 29 | */ |
| 26 | 30 | export const TEXT_COMPLETION_MODELS = [ |
| 27 | 31 | 'gpt-3.5-turbo-instruct', |
| 28 | 32 | 'gpt-3.5-turbo-instruct-0914', |
| 29 | 33 | 'text-davinci-003', |
| @@ -223,7 +227,7 @@ const commandTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyT | ||
| 223 | 227 | const qwen2Tokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/qwen2.json', 'src/tokenizers/llama3.json'); |
| 224 | 228 | const nemoTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/nemo.json', 'src/tokenizers/llama3.json'); |
| 225 | 229 | |
| 226 | 230 | export const sentencepieceTokenizers = [ |
| 227 | 231 | 'llama', |
| 228 | 232 | 'nerdstash', |
| 229 | 233 | 'nerdstash_v2', |
| @@ -238,7 +242,7 @@ const sentencepieceTokenizers = [ | ||
| 238 | 242 | * @param {string} model Sentencepiece model name |
| 239 | 243 | * @returns {SentencePieceTokenizer|null} Sentencepiece tokenizer |
| 240 | 244 | */ |
| 241 | 245 | export function getSentencepiceTokenizer(model) { |
| 242 | 246 | if (model.includes('llama')) { |
| 243 | 247 | return spp_llama; |
| 244 | 248 | } |
| @@ -350,7 +354,7 @@ function getWebTokenizersChunks(tokenizer, ids) { | ||
| 350 | 354 | * @param {string} requestModel Models to use for tokenization |
| 351 | 355 | * @returns {string} Tokenizer model to use |
| 352 | 356 | */ |
| 353 | 357 | export function getTokenizerModel(requestModel) { |
| 354 | 358 | if (requestModel.includes('o1-preview') || requestModel.includes('o1-mini')) { |
| 355 | 359 | return 'gpt-4o'; |
| 356 | 360 | } |
| @@ -427,7 +431,7 @@ function getTokenizerModel(requestModel) { | ||
| 427 | 431 | return 'gpt-3.5-turbo'; |
| 428 | 432 | } |
| 429 | 433 | |
| 430 | 434 | export function getTiktokenTokenizer(model) { |
| 431 | 435 | if (tokenizersCache[model]) { |
| 432 | 436 | return tokenizersCache[model]; |
| 433 | 437 | } |
| @@ -444,7 +448,7 @@ function getTiktokenTokenizer(model) { | ||
| 444 | 448 | * @param {object[]} messages Array of messages |
| 445 | 449 | * @returns {number} Number of tokens |
| 446 | 450 | */ |
| 447 | 451 | export function countWebTokenizerTokens(tokenizer, messages) { |
| 448 | 452 | // Should be fine if we use the old conversion method instead of the messages API one i think? |
| 449 | 453 | const convertedPrompt = convertClaudePrompt(messages, false, '', false, false, '', false); |
| 450 | 454 | |
| @@ -636,7 +640,7 @@ function createWebTokenizerDecodingHandler(tokenizer) { | ||
| 636 | 640 | }; |
| 637 | 641 | } |
| 638 | 642 | |
| 639 | 643 | export const router = express.Router(); |
| 640 | 644 | |
| 641 | 645 | router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama)); |
| 642 | 646 | router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd)); |
| @@ -1002,13 +1006,3 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re | ||
| 1002 | 1006 | return response.send({ error: true }); |
| 1003 | 1007 | } |
| 1004 | 1008 | }); |
| 1005 | - | |
| 1006 | -module.exports = { | |
| 1007 | - TEXT_COMPLETION_MODELS, | |
| 1008 | - getTokenizerModel, | |
| 1009 | - getTiktokenTokenizer, | |
| 1010 | - countWebTokenizerTokens, | |
| 1011 | - getSentencepiceTokenizer, | |
| 1012 | - sentencepieceTokenizers, | |
| 1013 | - router, | |
| 1014 | -}; | |
| @@ -1,29 +1,18 @@ | ||
| 1 | -const fetch = require('node-fetch').default; | |
| 1 | +import https from 'node:https'; | |
| 2 | -const https = require('https'); | |
| 2 | + | |
| 3 | -const express = require('express'); | |
| 3 | +import fetch from 'node-fetch'; | |
| 4 | -const iconv = require('iconv-lite'); | |
| 4 | +import express from 'express'; | |
| 5 | -const { readSecret, SECRET_KEYS } = require('./secrets'); | |
| 5 | +import bingTranslateApi from 'bing-translate-api'; | |
| 6 | -const { getConfigValue, uuidv4 } = require('../util'); | |
| 6 | +import googleTranslateApi from 'google-translate-api-x'; | |
| 7 | -const { jsonParser } = require('../express-common'); | |
| 7 | + | |
| 8 | +import { readSecret, SECRET_KEYS } from './secrets.js'; | |
| 9 | +import { getConfigValue, uuidv4 } from '../util.js'; | |
| 10 | +import { jsonParser } from '../express-common.js'; | |
| 8 | 11 | |
| 9 | 12 | const DEEPLX_URL_DEFAULT = 'http://127.0.0.1:1188/translate'; |
| 10 | 13 | const ONERING_URL_DEFAULT = 'http://127.0.0.1:4990/translate'; |
| 11 | 14 | |
| 12 | -/** | |
| 15 | +export const router = express.Router(); | |
| 13 | - * Tries to decode a Node.js Buffer to a string using iconv-lite for UTF-8. | |
| 14 | - * @param {Buffer} buffer Node.js Buffer | |
| 15 | - * @returns {string} Decoded string | |
| 16 | - */ | |
| 17 | -function decodeBuffer(buffer) { | |
| 18 | - try { | |
| 19 | - return iconv.decode(buffer, 'utf-8'); | |
| 20 | - } catch (error) { | |
| 21 | - console.log('Failed to decode buffer:', error); | |
| 22 | - return buffer.toString('utf-8'); | |
| 23 | - } | |
| 24 | -} | |
| 25 | - | |
| 26 | -const router = express.Router(); | |
| 27 | 16 | |
| 28 | 17 | router.post('/libre', jsonParser, async (request, response) => { |
| 29 | 18 | const key = readSecret(request.user.directories, SECRET_KEYS.LIBRE); |
| @@ -70,6 +59,7 @@ router.post('/libre', jsonParser, async (request, response) => { | ||
| 70 | 59 | return response.sendStatus(result.status); |
| 71 | 60 | } |
| 72 | 61 | |
| 62 | + /** @type {any} */ | |
| 73 | 63 | const json = await result.json(); |
| 74 | 64 | console.log('Translated text: ' + json.translatedText); |
| 75 | 65 | |
| @@ -82,7 +72,6 @@ router.post('/libre', jsonParser, async (request, response) => { | ||
| 82 | 72 | |
| 83 | 73 | router.post('/google', jsonParser, async (request, response) => { |
| 84 | 74 | try { |
| 85 | - const { generateRequestUrl, normaliseResponse } = require('google-translate-api-browser'); | |
| 86 | 75 | const text = request.body.text; |
| 87 | 76 | const lang = request.body.lang; |
| 88 | 77 | |
| @@ -92,31 +81,12 @@ router.post('/google', jsonParser, async (request, response) => { | ||
| 92 | 81 | |
| 93 | 82 | console.log('Input text: ' + text); |
| 94 | 83 | |
| 95 | 84 | const urlresult = generateRequestUrlawait googleTranslateApi(text, { to: lang, forceBatch: false }); |
| 85 | + const translatedText = Array.isArray(result) ? result.map(x => x.text).join('') : result.text; | |
| 96 | 86 | |
| 97 | - https.get(url, (resp) => { | |
| 87 | + response.setHeader('Content-Type', 'text/plain; charset=utf-8'); | |
| 98 | - const data = []; | |
| 88 | + console.log('Translated text: ' + translatedText); | |
| 99 | - | |
| 89 | + return response.send(translatedText); | |
| 100 | - resp.on('data', (chunk) => { | |
| 101 | - data.push(chunk); | |
| 102 | - }); | |
| 103 | - | |
| 104 | - resp.on('end', () => { | |
| 105 | - try { | |
| 106 | - const decodedData = decodeBuffer(Buffer.concat(data)); | |
| 107 | - const result = normaliseResponse(JSON.parse(decodedData)); | |
| 108 | - console.log('Translated text: ' + result.text); | |
| 109 | - response.setHeader('Content-Type', 'text/plain; charset=utf-8'); | |
| 110 | - return response.send(result.text); | |
| 111 | - } catch (error) { | |
| 112 | - console.log('Translation error', error); | |
| 113 | - return response.sendStatus(500); | |
| 114 | - } | |
| 115 | - }); | |
| 116 | - }).on('error', (err) => { | |
| 117 | - console.log('Translation error: ' + err.message); | |
| 118 | - return response.sendStatus(500); | |
| 119 | - }); | |
| 120 | 90 | } catch (error) { |
| 121 | 91 | console.log('Translation error', error); |
| 122 | 92 | return response.sendStatus(500); |
| @@ -151,7 +121,6 @@ router.post('/yandex', jsonParser, async (request, response) => { | ||
| 151 | 121 | headers: { |
| 152 | 122 | 'Content-Type': 'application/x-www-form-urlencoded', |
| 153 | 123 | }, |
| 154 | - timeout: 0, | |
| 155 | 124 | }); |
| 156 | 125 | |
| 157 | 126 | if (!result.ok) { |
| @@ -160,6 +129,7 @@ router.post('/yandex', jsonParser, async (request, response) => { | ||
| 160 | 129 | return response.sendStatus(500); |
| 161 | 130 | } |
| 162 | 131 | |
| 132 | + /** @type {any} */ | |
| 163 | 133 | const json = await result.json(); |
| 164 | 134 | const translated = json.text.join(); |
| 165 | 135 | console.log('Translated text: ' + translated); |
| @@ -257,7 +227,6 @@ router.post('/deepl', jsonParser, async (request, response) => { | ||
| 257 | 227 | 'Authorization': `DeepL-Auth-Key ${key}`, |
| 258 | 228 | 'Content-Type': 'application/x-www-form-urlencoded', |
| 259 | 229 | }, |
| 260 | - timeout: 0, | |
| 261 | 230 | }); |
| 262 | 231 | |
| 263 | 232 | if (!result.ok) { |
| @@ -266,6 +235,7 @@ router.post('/deepl', jsonParser, async (request, response) => { | ||
| 266 | 235 | return response.sendStatus(result.status); |
| 267 | 236 | } |
| 268 | 237 | |
| 238 | + /** @type {any} */ | |
| 269 | 239 | const json = await result.json(); |
| 270 | 240 | console.log('Translated text: ' + json.translations[0].text); |
| 271 | 241 | |
| @@ -310,7 +280,6 @@ router.post('/onering', jsonParser, async (request, response) => { | ||
| 310 | 280 | |
| 311 | 281 | const result = await fetch(fetchUrl, { |
| 312 | 282 | method: 'GET', |
| 313 | - timeout: 0, | |
| 314 | 283 | }); |
| 315 | 284 | |
| 316 | 285 | if (!result.ok) { |
| @@ -319,6 +288,7 @@ router.post('/onering', jsonParser, async (request, response) => { | ||
| 319 | 288 | return response.sendStatus(result.status); |
| 320 | 289 | } |
| 321 | 290 | |
| 291 | + /** @type {any} */ | |
| 322 | 292 | const data = await result.json(); |
| 323 | 293 | console.log('Translated text: ' + data.result); |
| 324 | 294 | |
| @@ -366,7 +336,6 @@ router.post('/deeplx', jsonParser, async (request, response) => { | ||
| 366 | 336 | 'Accept': 'application/json', |
| 367 | 337 | 'Content-Type': 'application/json', |
| 368 | 338 | }, |
| 369 | - timeout: 0, | |
| 370 | 339 | }); |
| 371 | 340 | |
| 372 | 341 | if (!result.ok) { |
| @@ -375,6 +344,7 @@ router.post('/deeplx', jsonParser, async (request, response) => { | ||
| 375 | 344 | return response.sendStatus(result.status); |
| 376 | 345 | } |
| 377 | 346 | |
| 347 | + /** @type {any} */ | |
| 378 | 348 | const json = await result.json(); |
| 379 | 349 | console.log('Translated text: ' + json.data); |
| 380 | 350 | |
| @@ -386,7 +356,6 @@ router.post('/deeplx', jsonParser, async (request, response) => { | ||
| 386 | 356 | }); |
| 387 | 357 | |
| 388 | 358 | router.post('/bing', jsonParser, async (request, response) => { |
| 389 | - const bingTranslateApi = require('bing-translate-api'); | |
| 390 | 359 | const text = request.body.text; |
| 391 | 360 | let lang = request.body.lang; |
| 392 | 361 | |
| @@ -408,5 +377,3 @@ router.post('/bing', jsonParser, async (request, response) => { | ||
| 408 | 377 | return response.sendStatus(500); |
| 409 | 378 | }); |
| 410 | 379 | }); |
| 411 | - | |
| 412 | -module.exports = { router }; | |
| @@ -1,10 +1,11 @@ | ||
| 1 | -const fsPromises = require('fs').promises; | |
| 1 | +import { promises as fsPromises } from 'node:fs'; | |
| 2 | -const storage = require('node-persist'); | |
| 2 | + | |
| 3 | -const express = require('express'); | |
| 3 | +import storage from 'node-persist'; | |
| 4 | 4 | constimport lodashexpress =from require('lodashexpress'); |
| 5 | -const { jsonParser } = require('../express-common'); | |
| 5 | +import lodash from 'lodash'; | |
| 6 | 6 | constimport { checkForNewContentjsonParser } =from require('../contentexpress-managercommon.js'); |
| 7 | -const { | |
| 7 | +import { checkForNewContent } from './content-manager.js'; | |
| 8 | +import { | |
| 8 | 9 | KEY_PREFIX, |
| 9 | 10 | toKey, |
| 10 | 11 | requireAdminMiddleware, |
| @@ -14,17 +15,17 @@ const { | ||
| 14 | 15 | getPasswordHash, |
| 15 | 16 | getUserDirectories, |
| 16 | 17 | ensurePublicDirectoriesExist, |
| 17 | 18 | } =from require('../users.js'); |
| 18 | 19 | constimport { DEFAULT_USER } =from require('../constants.js'); |
| 19 | 20 | |
| 20 | 21 | export const router = express.Router(); |
| 21 | 22 | |
| 22 | 23 | router.post('/get', requireAdminMiddleware, jsonParser, async (_request, response) => { |
| 23 | 24 | try { |
| 24 | 25 | /** @type {import('../users.js').User[]} */ |
| 25 | 26 | const users = await storage.values(x => x.key.startsWith(KEY_PREFIX)); |
| 26 | 27 | |
| 27 | 28 | /** @type {Promise<import('../users.js').UserViewModel>[]} */ |
| 28 | 29 | const viewModelPromises = users |
| 29 | 30 | .map(user => new Promise(resolve => { |
| 30 | 31 | getUserAvatar(user.handle).then(avatar => |
| @@ -61,7 +62,7 @@ router.post('/disable', requireAdminMiddleware, jsonParser, async (request, resp | ||
| 61 | 62 | return response.status(400).json({ error: 'Cannot disable yourself' }); |
| 62 | 63 | } |
| 63 | 64 | |
| 64 | 65 | /** @type {import('../users.js').User} */ |
| 65 | 66 | const user = await storage.getItem(toKey(request.body.handle)); |
| 66 | 67 | |
| 67 | 68 | if (!user) { |
| @@ -85,7 +86,7 @@ router.post('/enable', requireAdminMiddleware, jsonParser, async (request, respo | ||
| 85 | 86 | return response.status(400).json({ error: 'Missing required fields' }); |
| 86 | 87 | } |
| 87 | 88 | |
| 88 | 89 | /** @type {import('../users.js').User} */ |
| 89 | 90 | const user = await storage.getItem(toKey(request.body.handle)); |
| 90 | 91 | |
| 91 | 92 | if (!user) { |
| @@ -109,7 +110,7 @@ router.post('/promote', requireAdminMiddleware, jsonParser, async (request, resp | ||
| 109 | 110 | return response.status(400).json({ error: 'Missing required fields' }); |
| 110 | 111 | } |
| 111 | 112 | |
| 112 | 113 | /** @type {import('../users.js').User} */ |
| 113 | 114 | const user = await storage.getItem(toKey(request.body.handle)); |
| 114 | 115 | |
| 115 | 116 | if (!user) { |
| @@ -138,7 +139,7 @@ router.post('/demote', requireAdminMiddleware, jsonParser, async (request, respo | ||
| 138 | 139 | return response.status(400).json({ error: 'Cannot demote yourself' }); |
| 139 | 140 | } |
| 140 | 141 | |
| 141 | 142 | /** @type {import('../users.js').User} */ |
| 142 | 143 | const user = await storage.getItem(toKey(request.body.handle)); |
| 143 | 144 | |
| 144 | 145 | if (!user) { |
| @@ -249,7 +250,3 @@ router.post('/slugify', requireAdminMiddleware, jsonParser, async (request, resp | ||
| 249 | 250 | return response.sendStatus(500); |
| 250 | 251 | } |
| 251 | 252 | }); |
| 252 | - | |
| 253 | -module.exports = { | |
| 254 | - router, | |
| 255 | -}; | |
| @@ -1,18 +1,19 @@ | ||
| 1 | 1 | constimport path =from require('node:path'); |
| 2 | -const fsPromises = require('fs').promises; | |
| 2 | +import { promises as fsPromises } from 'node:fs'; | |
| 3 | 3 | constimport storagecrypto =from require('node-persist:crypto'); |
| 4 | -const express = require('express'); | |
| 4 | + | |
| 5 | -const crypto = require('crypto'); | |
| 5 | +import storage from 'node-persist'; | |
| 6 | -const { jsonParser } = require('../express-common'); | |
| 6 | +import express from 'express'; | |
| 7 | -const { getUserAvatar, toKey, getPasswordHash, getPasswordSalt, createBackupArchive, ensurePublicDirectoriesExist, toAvatarKey } = require('../users'); | |
| 7 | + | |
| 8 | 8 | constimport { SETTINGS_FILEjsonParser } =from require('../constantsexpress-common.js'); |
| 9 | -const contentManager = require('./content-manager'); | |
| 9 | +import { getUserAvatar, toKey, getPasswordHash, getPasswordSalt, createBackupArchive, ensurePublicDirectoriesExist, toAvatarKey } from '../users.js'; | |
| 10 | 10 | constimport { color, CacheSETTINGS_FILE } =from require('../utilconstants.js'); |
| 11 | 11 | constimport { checkForNewContent, CONTENT_TYPES } =from require('./content-manager.js'); |
| 12 | +import { color, Cache } from '../util.js'; | |
| 12 | 13 | |
| 13 | 14 | const RESET_CACHE = new Cache(5 * 60 * 1000); |
| 14 | 15 | |
| 15 | 16 | export const router = express.Router(); |
| 16 | 17 | |
| 17 | 18 | router.post('/logout', async (request, response) => { |
| 18 | 19 | try { |
| @@ -70,7 +71,7 @@ router.post('/change-avatar', jsonParser, async (request, response) => { | ||
| 70 | 71 | return response.status(400).json({ error: 'Invalid data URL' }); |
| 71 | 72 | } |
| 72 | 73 | |
| 73 | 74 | /** @type {import('../users.js').User} */ |
| 74 | 75 | const user = await storage.getItem(toKey(request.body.handle)); |
| 75 | 76 | |
| 76 | 77 | if (!user) { |
| @@ -99,7 +100,7 @@ router.post('/change-password', jsonParser, async (request, response) => { | ||
| 99 | 100 | return response.status(403).json({ error: 'Unauthorized' }); |
| 100 | 101 | } |
| 101 | 102 | |
| 102 | 103 | /** @type {import('../users.js').User} */ |
| 103 | 104 | const user = await storage.getItem(toKey(request.body.handle)); |
| 104 | 105 | |
| 105 | 106 | if (!user) { |
| @@ -166,7 +167,7 @@ router.post('/reset-settings', jsonParser, async (request, response) => { | ||
| 166 | 167 | |
| 167 | 168 | const pathToFile = path.join(request.user.directories.root, SETTINGS_FILE); |
| 168 | 169 | await fsPromises.rm(pathToFile, { force: true }); |
| 169 | 170 | await contentManager.checkForNewContent([request.user.directories], [contentManager.CONTENT_TYPES.SETTINGS]); |
| 170 | 171 | |
| 171 | 172 | return response.sendStatus(204); |
| 172 | 173 | } catch (error) { |
| @@ -187,7 +188,7 @@ router.post('/change-name', jsonParser, async (request, response) => { | ||
| 187 | 188 | return response.status(403).json({ error: 'Unauthorized' }); |
| 188 | 189 | } |
| 189 | 190 | |
| 190 | 191 | /** @type {import('../users.js').User} */ |
| 191 | 192 | const user = await storage.getItem(toKey(request.body.handle)); |
| 192 | 193 | |
| 193 | 194 | if (!user) { |
| @@ -251,7 +252,3 @@ router.post('/reset-step2', jsonParser, async (request, response) => { | ||
| 251 | 252 | return response.sendStatus(500); |
| 252 | 253 | } |
| 253 | 254 | }); |
| 254 | - | |
| 255 | -module.exports = { | |
| 256 | - router, | |
| 257 | -}; | |
| @@ -1,15 +1,16 @@ | ||
| 1 | 1 | constimport crypto =from require('node:crypto'); |
| 2 | -const storage = require('node-persist'); | |
| 2 | + | |
| 3 | -const express = require('express'); | |
| 3 | +import storage from 'node-persist'; | |
| 4 | -const { RateLimiterMemory, RateLimiterRes } = require('rate-limiter-flexible'); | |
| 4 | +import express from 'express'; | |
| 5 | 5 | constimport { jsonParserRateLimiterMemory, getIpFromRequestRateLimiterRes } =from require('../expressrate-commonlimiter-flexible'); |
| 6 | 6 | constimport { color, CachejsonParser, getConfigValuegetIpFromRequest } =from require('../utilexpress-common.js'); |
| 7 | 7 | constimport { KEY_PREFIX, getUserAvatar, toKeycolor, getPasswordHashCache, getPasswordSaltgetConfigValue } =from require('../usersutil.js'); |
| 8 | +import { KEY_PREFIX, getUserAvatar, toKey, getPasswordHash, getPasswordSalt } from '../users.js'; | |
| 8 | 9 | |
| 9 | 10 | const DISCREET_LOGIN = getConfigValue('enableDiscreetLogin', false); |
| 10 | 11 | const MFA_CACHE = new Cache(5 * 60 * 1000); |
| 11 | 12 | |
| 12 | 13 | export const router = express.Router(); |
| 13 | 14 | const loginLimiter = new RateLimiterMemory({ |
| 14 | 15 | points: 5, |
| 15 | 16 | duration: 60, |
| @@ -25,10 +26,10 @@ router.post('/list', async (_request, response) => { | ||
| 25 | 26 | return response.sendStatus(204); |
| 26 | 27 | } |
| 27 | 28 | |
| 28 | 29 | /** @type {import('../users.js').User[]} */ |
| 29 | 30 | const users = await storage.values(x => x.key.startsWith(KEY_PREFIX)); |
| 30 | 31 | |
| 31 | 32 | /** @type {Promise<import('../users.js').UserViewModel>[]} */ |
| 32 | 33 | const viewModelPromises = users |
| 33 | 34 | .filter(x => x.enabled) |
| 34 | 35 | .map(user => new Promise(async (resolve) => { |
| @@ -62,7 +63,7 @@ router.post('/login', jsonParser, async (request, response) => { | ||
| 62 | 63 | const ip = getIpFromRequest(request); |
| 63 | 64 | await loginLimiter.consume(ip); |
| 64 | 65 | |
| 65 | 66 | /** @type {import('../users.js').User} */ |
| 66 | 67 | const user = await storage.getItem(toKey(request.body.handle)); |
| 67 | 68 | |
| 68 | 69 | if (!user) { |
| @@ -110,7 +111,7 @@ router.post('/recover-step1', jsonParser, async (request, response) => { | ||
| 110 | 111 | const ip = getIpFromRequest(request); |
| 111 | 112 | await recoverLimiter.consume(ip); |
| 112 | 113 | |
| 113 | 114 | /** @type {import('../users.js').User} */ |
| 114 | 115 | const user = await storage.getItem(toKey(request.body.handle)); |
| 115 | 116 | |
| 116 | 117 | if (!user) { |
| @@ -147,7 +148,7 @@ router.post('/recover-step2', jsonParser, async (request, response) => { | ||
| 147 | 148 | return response.status(400).json({ error: 'Missing required fields' }); |
| 148 | 149 | } |
| 149 | 150 | |
| 150 | 151 | /** @type {import('../users.js').User} */ |
| 151 | 152 | const user = await storage.getItem(toKey(request.body.handle)); |
| 152 | 153 | const ip = getIpFromRequest(request); |
| 153 | 154 | |
| @@ -193,7 +194,3 @@ router.post('/recover-step2', jsonParser, async (request, response) => { | ||
| 193 | 194 | return response.sendStatus(500); |
| 194 | 195 | } |
| 195 | 196 | }); |
| 196 | - | |
| 197 | -module.exports = { | |
| 198 | - router, | |
| 199 | -}; | |
| @@ -1,10 +1,22 @@ | ||
| 1 | -const vectra = require('vectra'); | |
| 1 | +import path from 'node:path'; | |
| 2 | -const path = require('path'); | |
| 2 | +import fs from 'node:fs'; | |
| 3 | -const fs = require('fs'); | |
| 3 | + | |
| 4 | 4 | constimport expressvectra =from require('expressvectra'); |
| 5 | -const sanitize = require('sanitize-filename'); | |
| 5 | +import express from 'express'; | |
| 6 | -const { jsonParser } = require('../express-common'); | |
| 6 | +import sanitize from 'sanitize-filename'; | |
| 7 | -const { getConfigValue } = require('../util'); | |
| 7 | + | |
| 8 | +import { jsonParser } from '../express-common.js'; | |
| 9 | +import { getConfigValue } from '../util.js'; | |
| 10 | + | |
| 11 | +import { getNomicAIBatchVector, getNomicAIVector } from '../vectors/nomicai-vectors.js'; | |
| 12 | +import { getOpenAIVector, getOpenAIBatchVector } from '../vectors/openai-vectors.js'; | |
| 13 | +import { getTransformersVector, getTransformersBatchVector } from '../vectors/embedding.js'; | |
| 14 | +import { getExtrasVector, getExtrasBatchVector } from '../vectors/extras-vectors.js'; | |
| 15 | +import { getMakerSuiteVector, getMakerSuiteBatchVector } from '../vectors/makersuite-vectors.js'; | |
| 16 | +import { getCohereVector, getCohereBatchVector } from '../vectors/cohere-vectors.js'; | |
| 17 | +import { getLlamaCppVector, getLlamaCppBatchVector } from '../vectors/llamacpp-vectors.js'; | |
| 18 | +import { getVllmVector, getVllmBatchVector } from '../vectors/vllm-vectors.js'; | |
| 19 | +import { getOllamaVector, getOllamaBatchVector } from '../vectors/ollama-vectors.js'; | |
| 8 | 20 | |
| 9 | 21 | // Don't forget to add new sources to the SOURCES array |
| 10 | 22 | const SOURCES = [ |
| @@ -27,31 +39,31 @@ const SOURCES = [ | ||
| 27 | 39 | * @param {Object} sourceSettings - Settings for the source, if it needs any |
| 28 | 40 | * @param {string} text - The text to get the vector for |
| 29 | 41 | * @param {boolean} isQuery - If the text is a query for embedding search |
| 30 | 42 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 31 | 43 | * @returns {Promise<number[]>} - The vector for the text |
| 32 | 44 | */ |
| 33 | 45 | async function getVector(source, sourceSettings, text, isQuery, directories) { |
| 34 | 46 | switch (source) { |
| 35 | 47 | case 'nomicai': |
| 36 | 48 | return require('../vectors/nomicai-vectors').getNomicAIVector(text, source, directories); |
| 37 | 49 | case 'togetherai': |
| 38 | 50 | case 'mistral': |
| 39 | 51 | case 'openai': |
| 40 | 52 | return require('../vectors/openai-vectors').getOpenAIVector(text, source, directories, sourceSettings.model); |
| 41 | 53 | case 'transformers': |
| 42 | 54 | return require('../vectors/embedding').getTransformersVector(text); |
| 43 | 55 | case 'extras': |
| 44 | 56 | return require('../vectors/extras-vectors').getExtrasVector(text, sourceSettings.extrasUrl, sourceSettings.extrasKey); |
| 45 | 57 | case 'palm': |
| 46 | 58 | return require('../vectors/makersuite-vectors').getMakerSuiteVector(text, directories); |
| 47 | 59 | case 'cohere': |
| 48 | 60 | return require('../vectors/cohere-vectors').getCohereVector(text, isQuery, directories, sourceSettings.model); |
| 49 | 61 | case 'llamacpp': |
| 50 | 62 | return require('../vectors/llamacpp-vectors').getLlamaCppVector(text, sourceSettings.apiUrl, directories); |
| 51 | 63 | case 'vllm': |
| 52 | 64 | return require('../vectors/vllm-vectors').getVllmVector(text, sourceSettings.apiUrl, sourceSettings.model, directories); |
| 53 | 65 | case 'ollama': |
| 54 | 66 | return require('../vectors/ollama-vectors').getOllamaVector(text, sourceSettings.apiUrl, sourceSettings.model, sourceSettings.keep, directories); |
| 55 | 67 | } |
| 56 | 68 | |
| 57 | 69 | throw new Error(`Unknown vector source ${source}`); |
| @@ -63,7 +75,7 @@ async function getVector(source, sourceSettings, text, isQuery, directories) { | ||
| 63 | 75 | * @param {Object} sourceSettings - Settings for the source, if it needs any |
| 64 | 76 | * @param {string[]} texts - The array of texts to get the vector for |
| 65 | 77 | * @param {boolean} isQuery - If the text is a query for embedding search |
| 66 | 78 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 67 | 79 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 68 | 80 | */ |
| 69 | 81 | async function getBatchVector(source, sourceSettings, texts, isQuery, directories) { |
| @@ -74,33 +86,33 @@ async function getBatchVector(source, sourceSettings, texts, isQuery, directorie | ||
| 74 | 86 | for (let batch of batches) { |
| 75 | 87 | switch (source) { |
| 76 | 88 | case 'nomicai': |
| 77 | 89 | results.push(...await require('../vectors/nomicai-vectors').getNomicAIBatchVector(batch, source, directories)); |
| 78 | 90 | break; |
| 79 | 91 | case 'togetherai': |
| 80 | 92 | case 'mistral': |
| 81 | 93 | case 'openai': |
| 82 | 94 | results.push(...await require('../vectors/openai-vectors').getOpenAIBatchVector(batch, source, directories, sourceSettings.model)); |
| 83 | 95 | break; |
| 84 | 96 | case 'transformers': |
| 85 | 97 | results.push(...await require('../vectors/embedding').getTransformersBatchVector(batch)); |
| 86 | 98 | break; |
| 87 | 99 | case 'extras': |
| 88 | 100 | results.push(...await require('../vectors/extras-vectors').getExtrasBatchVector(batch, sourceSettings.extrasUrl, sourceSettings.extrasKey)); |
| 89 | 101 | break; |
| 90 | 102 | case 'palm': |
| 91 | 103 | results.push(...await require('../vectors/makersuite-vectors').getMakerSuiteBatchVector(batch, directories)); |
| 92 | 104 | break; |
| 93 | 105 | case 'cohere': |
| 94 | 106 | results.push(...await require('../vectors/cohere-vectors').getCohereBatchVector(batch, isQuery, directories, sourceSettings.model)); |
| 95 | 107 | break; |
| 96 | 108 | case 'llamacpp': |
| 97 | 109 | results.push(...await require('../vectors/llamacpp-vectors').getLlamaCppBatchVector(batch, sourceSettings.apiUrl, directories)); |
| 98 | 110 | break; |
| 99 | 111 | case 'vllm': |
| 100 | 112 | results.push(...await require('../vectors/vllm-vectors').getVllmBatchVector(batch, sourceSettings.apiUrl, sourceSettings.model, directories)); |
| 101 | 113 | break; |
| 102 | 114 | case 'ollama': |
| 103 | 115 | results.push(...await require('../vectors/ollama-vectors').getOllamaBatchVector(batch, sourceSettings.apiUrl, sourceSettings.model, sourceSettings.keep, directories)); |
| 104 | 116 | break; |
| 105 | 117 | default: |
| 106 | 118 | throw new Error(`Unknown vector source ${source}`); |
| @@ -183,7 +195,7 @@ function getModelScope(sourceSettings) { | ||
| 183 | 195 | |
| 184 | 196 | /** |
| 185 | 197 | * Gets the index for the vector collection |
| 186 | 198 | * @param {import('../users.js').UserDirectoryList} directories - User directories |
| 187 | 199 | * @param {string} collectionId - The collection ID |
| 188 | 200 | * @param {string} source - The source of the vector |
| 189 | 201 | * @param {object} sourceSettings - The model for the source |
| @@ -203,7 +215,7 @@ async function getIndex(directories, collectionId, source, sourceSettings) { | ||
| 203 | 215 | |
| 204 | 216 | /** |
| 205 | 217 | * Inserts items into the vector collection |
| 206 | 218 | * @param {import('../users.js').UserDirectoryList} directories - User directories |
| 207 | 219 | * @param {string} collectionId - The collection ID |
| 208 | 220 | * @param {string} source - The source of the vector |
| 209 | 221 | * @param {Object} sourceSettings - Settings for the source, if it needs any |
| @@ -227,7 +239,7 @@ async function insertVectorItems(directories, collectionId, source, sourceSettin | ||
| 227 | 239 | |
| 228 | 240 | /** |
| 229 | 241 | * Gets the hashes of the items in the vector collection |
| 230 | 242 | * @param {import('../users.js').UserDirectoryList} directories - User directories |
| 231 | 243 | * @param {string} collectionId - The collection ID |
| 232 | 244 | * @param {string} source - The source of the vector |
| 233 | 245 | * @param {Object} sourceSettings - Settings for the source, if it needs any |
| @@ -244,7 +256,7 @@ async function getSavedHashes(directories, collectionId, source, sourceSettings) | ||
| 244 | 256 | |
| 245 | 257 | /** |
| 246 | 258 | * Deletes items from the vector collection by hash |
| 247 | 259 | * @param {import('../users.js').UserDirectoryList} directories - User directories |
| 248 | 260 | * @param {string} collectionId - The collection ID |
| 249 | 261 | * @param {string} source - The source of the vector |
| 250 | 262 | * @param {Object} sourceSettings - Settings for the source, if it needs any |
| @@ -265,7 +277,7 @@ async function deleteVectorItems(directories, collectionId, source, sourceSettin | ||
| 265 | 277 | |
| 266 | 278 | /** |
| 267 | 279 | * Gets the hashes of the items in the vector collection that match the search text |
| 268 | 280 | * @param {import('../users.js').UserDirectoryList} directories - User directories |
| 269 | 281 | * @param {string} collectionId - The collection ID |
| 270 | 282 | * @param {string} source - The source of the vector |
| 271 | 283 | * @param {Object} sourceSettings - Settings for the source, if it needs any |
| @@ -286,7 +298,7 @@ async function queryCollection(directories, collectionId, source, sourceSettings | ||
| 286 | 298 | |
| 287 | 299 | /** |
| 288 | 300 | * Queries multiple collections for the given search queries. Returns the overall top K results. |
| 289 | 301 | * @param {import('../users.js').UserDirectoryList} directories - User directories |
| 290 | 302 | * @param {string[]} collectionIds - The collection IDs to query |
| 291 | 303 | * @param {string} source - The source of the vector |
| 292 | 304 | * @param {Object} sourceSettings - Settings for the source, if it needs any |
| @@ -359,7 +371,7 @@ async function regenerateCorruptedIndexErrorHandler(req, res, error) { | ||
| 359 | 371 | return res.sendStatus(500); |
| 360 | 372 | } |
| 361 | 373 | |
| 362 | 374 | export const router = express.Router(); |
| 363 | 375 | |
| 364 | 376 | router.post('/query', jsonParser, async (req, res) => { |
| 365 | 377 | try { |
| @@ -495,5 +507,3 @@ router.post('/purge', jsonParser, async (req, res) => { | ||
| 495 | 507 | return res.sendStatus(500); |
| 496 | 508 | } |
| 497 | 509 | }); |
| 498 | - | |
| 499 | -module.exports = { router }; | |
| @@ -1,19 +1,20 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | -const express = require('express'); | |
| 4 | -const sanitize = require('sanitize-filename'); | |
| 5 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 6 | 3 | |
| 7 | -const { jsonParser, urlencodedParser } = require('../express-common'); | |
| 4 | +import express from 'express'; | |
| 5 | +import sanitize from 'sanitize-filename'; | |
| 6 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 7 | + | |
| 8 | +import { jsonParser, urlencodedParser } from '../express-common.js'; | |
| 8 | 9 | |
| 9 | 10 | /** |
| 10 | 11 | * Reads a World Info file and returns its contents |
| 11 | 12 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 12 | 13 | * @param {string} worldInfoName Name of the World Info file |
| 13 | 14 | * @param {boolean} allowDummy If true, returns an empty object if the file doesn't exist |
| 14 | 15 | * @returns {object} World Info file contents |
| 15 | 16 | */ |
| 16 | 17 | export function readWorldInfoFile(directories, worldInfoName, allowDummy) { |
| 17 | 18 | const dummyObject = allowDummy ? { entries: {} } : null; |
| 18 | 19 | |
| 19 | 20 | if (!worldInfoName) { |
| @@ -33,7 +34,7 @@ function readWorldInfoFile(directories, worldInfoName, allowDummy) { | ||
| 33 | 34 | return worldInfo; |
| 34 | 35 | } |
| 35 | 36 | |
| 36 | 37 | export const router = express.Router(); |
| 37 | 38 | |
| 38 | 39 | router.post('/get', jsonParser, (request, response) => { |
| 39 | 40 | if (!request.body?.name) { |
| @@ -122,5 +123,3 @@ router.post('/edit', jsonParser, (request, response) => { | ||
| 122 | 123 | |
| 123 | 124 | return response.send({ ok: true }); |
| 124 | 125 | }); |
| 125 | - | |
| 126 | -module.exports = { router, readWorldInfoFile }; | |
| @@ -1,28 +1,27 @@ | ||
| 1 | 1 | constimport express =from require('express'); |
| 2 | 2 | constimport ipaddr =from require('ipaddr.js'); |
| 3 | 3 | |
| 4 | 4 | // Instantiate parser middleware here with application-level size limits |
| 5 | 5 | export const jsonParser = express.json({ limit: '200mb' }); |
| 6 | 6 | export const urlencodedParser = express.urlencoded({ extended: true, limit: '200mb' }); |
| 7 | 7 | |
| 8 | 8 | /** |
| 9 | 9 | * Gets the IP address of the client from the request object. |
| 10 | 10 | * @param {import('express').Request)} req Request object |
| 11 | 11 | * @returns {string} IP address of the client |
| 12 | 12 | */ |
| 13 | 13 | export function getIpFromRequest(req) { |
| 14 | 14 | let clientIp = req.connectionsocket.remoteAddress; |
| 15 | + if (!clientIp) { | |
| 16 | + return 'unknown'; | |
| 17 | + } | |
| 15 | 18 | let ip = ipaddr.parse(clientIp); |
| 16 | 19 | // Check if the IP address is IPv4-mapped IPv6 address |
| 17 | 20 | if (ip.kind() === 'ipv6' && ip instanceof ipaddr.IPv6 && ip.isIPv4MappedAddress()) { |
| 18 | 21 | const ipv4 = ip.toIPv4Address().toString(); |
| 19 | 22 | clientIp = ipv4; |
| 20 | 23 | } else { |
| 21 | 24 | clientIp = ip.toString(); |
| 22 | - clientIp = clientIp.toString(); | |
| 23 | 25 | } |
| 24 | 26 | return clientIp; |
| 25 | 27 | } |
| 26 | - | |
| 27 | - | |
| 28 | -module.exports = { jsonParser, urlencodedParser, getIpFromRequest }; | |
| @@ -2,9 +2,10 @@ | ||
| 2 | 2 | * When applied, this middleware will ensure the request contains the required header for basic authentication and only |
| 3 | 3 | * allow access to the endpoint after successful authentication. |
| 4 | 4 | */ |
| 5 | -const { getAllUserHandles, toKey, getPasswordHash } = require('../users.js'); | |
| 5 | +import { Buffer } from 'node:buffer'; | |
| 6 | -const { getConfig, getConfigValue } = require('../util.js'); | |
| 6 | +import storage from 'node-persist'; | |
| 7 | -const storage = require('node-persist'); | |
| 7 | +import { getAllUserHandles, toKey, getPasswordHash } from '../users.js'; | |
| 8 | +import { getConfig, getConfigValue } from '../util.js'; | |
| 8 | 9 | |
| 9 | 10 | const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false); |
| 10 | 11 | const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false); |
| @@ -49,4 +50,4 @@ const basicAuthMiddleware = async function (request, response, callback) { | ||
| 49 | 50 | return unauthorizedResponse(response); |
| 50 | 51 | }; |
| 51 | 52 | |
| 52 | 53 | module.exportsexport =default basicAuthMiddleware; |
| @@ -1,3 +1,5 @@ | ||
| 1 | +import { Buffer } from 'node:buffer'; | |
| 2 | + | |
| 1 | 3 | /** |
| 2 | 4 | * Decodes a file name from Latin1 to UTF-8. |
| 3 | 5 | * @param {string} str Input string |
| @@ -14,7 +16,7 @@ function decodeFileName(str) { | ||
| 14 | 16 | * @param {import('express').Response} _res Response |
| 15 | 17 | * @param {import('express').NextFunction} next Next middleware |
| 16 | 18 | */ |
| 17 | 19 | export default function multerMonkeyPatch(req, _res, next) { |
| 18 | 20 | try { |
| 19 | 21 | if (req.file) { |
| 20 | 22 | req.file.originalname = decodeFileName(req.file.originalname); |
| @@ -26,5 +28,3 @@ function multerMonkeyPatch(req, _res, next) { | ||
| 26 | 28 | next(); |
| 27 | 29 | } |
| 28 | 30 | } |
| 29 | - | |
| 30 | -module.exports = multerMonkeyPatch; | |
| @@ -1,9 +1,10 @@ | ||
| 1 | 1 | constimport path =from require('node:path'); |
| 2 | 2 | constimport fs =from require('node:fs'); |
| 3 | -const ipMatching = require('ip-matching'); | |
| 3 | +import process from 'node:process'; | |
| 4 | +import ipMatching from 'ip-matching'; | |
| 4 | 5 | |
| 5 | 6 | constimport { getIpFromRequest } =from require('../express-common.js'); |
| 6 | 7 | constimport { color, getConfigValue } =from require('../util.js'); |
| 7 | 8 | |
| 8 | 9 | const whitelistPath = path.join(process.cwd(), './whitelist.txt'); |
| 9 | 10 | const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false); |
| @@ -50,7 +51,7 @@ function getForwardedIp(req) { | ||
| 50 | 51 | * @param {boolean} listen If listen mode is enabled via config or command line |
| 51 | 52 | * @returns {import('express').RequestHandler} The middleware function |
| 52 | 53 | */ |
| 53 | 54 | export default function whitelistMiddleware(whitelistMode, listen) { |
| 54 | 55 | return function (req, res, next) { |
| 55 | 56 | const clientIp = getIpFromRequest(req); |
| 56 | 57 | const forwardedIp = getForwardedIp(req); |
| @@ -82,5 +83,3 @@ function whitelistMiddleware(whitelistMode, listen) { | ||
| 82 | 83 | next(); |
| 83 | 84 | }; |
| 84 | 85 | } |
| 85 | - | |
| 86 | -module.exports = whitelistMiddleware; | |
| @@ -1,8 +1,9 @@ | ||
| 1 | 1 | constimport fs =from require('node:fs'); |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | 3 | constimport url =from require('node:url'); |
| 4 | -const express = require('express'); | |
| 4 | + | |
| 5 | -const { getConfigValue } = require('./util'); | |
| 5 | +import express from 'express'; | |
| 6 | +import { getConfigValue } from './util.js'; | |
| 6 | 7 | const enableServerPlugins = getConfigValue('enableServerPlugins', false); |
| 7 | 8 | |
| 8 | 9 | /** |
| @@ -16,7 +17,7 @@ const loadedPlugins = new Map(); | ||
| 16 | 17 | * @param {string} file Path to file |
| 17 | 18 | * @returns {boolean} True if file is a CommonJS module |
| 18 | 19 | */ |
| 19 | 20 | const isCommonJS = (file) => path.extname(file) === '.js' || path.extname(file) === '.cjs'; |
| 20 | 21 | |
| 21 | 22 | /** |
| 22 | 23 | * Determine if a file is an ECMAScript module. |
| @@ -32,9 +33,9 @@ const isESModule = (file) => path.extname(file) === '.mjs'; | ||
| 32 | 33 | * @returns {Promise<Function>} Promise that resolves when all plugins are loaded. Resolves to a "cleanup" function to |
| 33 | 34 | * be called before the server shuts down. |
| 34 | 35 | */ |
| 35 | 36 | export async function loadPlugins(app, pluginsPath) { |
| 36 | 37 | const exitHooks = []; |
| 37 | 38 | const emptyFn = () => { }; |
| 38 | 39 | |
| 39 | 40 | // Server plugins are disabled. |
| 40 | 41 | if (!enableServerPlugins) { |
| @@ -89,19 +90,15 @@ async function loadFromDirectory(app, pluginDirectoryPath, exitHooks) { | ||
| 89 | 90 | } |
| 90 | 91 | } |
| 91 | 92 | |
| 92 | 93 | // Plugin is a CommonJS module file. |
| 93 | 94 | const cjsFilePathfileTypes = path['index.join(pluginDirectoryPathjs', 'index.jscjs'), 'index.mjs']; |
| 94 | - if (fs.existsSync(cjsFilePath)) { | |
| 95 | - if (await loadFromFile(app, cjsFilePath, exitHooks)) { | |
| 96 | - return; | |
| 97 | - } | |
| 98 | - } | |
| 99 | 95 | |
| 100 | - // Plugin is an ECMAScript module. | |
| 96 | + for (const fileType of fileTypes) { | |
| 101 | 97 | const esmFilePathfilePath = path.join(pluginDirectoryPath, 'index.mjs'fileType); |
| 102 | 98 | if (fs.existsSync(esmFilePathfilePath)) { |
| 103 | 99 | if (await loadFromFile(app, esmFilePathfilePath, exitHooks)) { |
| 104 | 100 | return; |
| 101 | + } | |
| 105 | 102 | } |
| 106 | 103 | } |
| 107 | 104 | } |
| @@ -217,7 +214,3 @@ async function initPlugin(app, plugin, exitHooks) { | ||
| 217 | 214 | |
| 218 | 215 | return true; |
| 219 | 216 | } |
| 220 | - | |
| 221 | -module.exports = { | |
| 222 | - loadPlugins, | |
| 223 | -}; | |
| @@ -1,10 +0,0 @@ | ||
| 1 | -if (!Array.prototype.findLastIndex) { | |
| 2 | - Array.prototype.findLastIndex = function (callback, thisArg) { | |
| 3 | - for (let i = this.length - 1; i >= 0; i--) { | |
| 4 | - if (callback.call(thisArg, this[i], i, this)) return i; | |
| 5 | - } | |
| 6 | - return -1; | |
| 7 | - }; | |
| 8 | -} | |
| 9 | - | |
| 10 | -module.exports = {}; | |
| @@ -1,6 +1,5 @@ | ||
| 1 | -require('./polyfill.js'); | |
| 1 | +import crypto from 'node:crypto'; | |
| 2 | 2 | constimport { getConfigValue } =from require('./util.js'); |
| 3 | -const crypto = require('crypto'); | |
| 4 | 3 | |
| 5 | 4 | const PROMPT_PLACEHOLDER = getConfigValue('promptPlaceholder', 'Let\'s get started.'); |
| 6 | 5 | |
| @@ -17,7 +16,7 @@ const PROMPT_PLACEHOLDER = getConfigValue('promptPlaceholder', 'Let\'s get start | ||
| 17 | 16 | * @returns {string} Prompt for Claude |
| 18 | 17 | * @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3). |
| 19 | 18 | */ |
| 20 | 19 | export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg, excludePrefixes) { |
| 21 | 20 | |
| 22 | 21 | //Prepare messages for claude. |
| 23 | 22 | //When 'Exclude Human/Assistant prefixes' checked, setting messages role to the 'system'(last message is exception). |
| @@ -96,7 +95,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, | ||
| 96 | 95 | * @param {string} charName Character name |
| 97 | 96 | * @param {string} userName User name |
| 98 | 97 | */ |
| 99 | 98 | export function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, humanMsgFix, charName = '', userName = '') { |
| 100 | 99 | let systemPrompt = []; |
| 101 | 100 | if (useSysPrompt) { |
| 102 | 101 | // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array. |
| @@ -279,7 +278,7 @@ function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, | ||
| 279 | 278 | * @param {string} userName User name |
| 280 | 279 | * @returns {{chatHistory: object[]}} Prompt for Cohere |
| 281 | 280 | */ |
| 282 | 281 | export function convertCohereMessages(messages, charName = '', userName = '') { |
| 283 | 282 | if (messages.length === 0) { |
| 284 | 283 | messages.unshift({ |
| 285 | 284 | role: 'user', |
| @@ -333,7 +332,7 @@ function convertCohereMessages(messages, charName = '', userName = '') { | ||
| 333 | 332 | * @param {string} userName User name |
| 334 | 333 | * @returns {{contents: *[], system_instruction: {parts: {text: string}}}} Prompt for Google MakerSuite models |
| 335 | 334 | */ |
| 336 | 335 | export function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '', userName = '') { |
| 337 | 336 | // This is a 1x1 transparent PNG |
| 338 | 337 | const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; |
| 339 | 338 | |
| @@ -456,7 +455,7 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = ' | ||
| 456 | 455 | * @param {string} charName Character name |
| 457 | 456 | * @param {string} userName User name |
| 458 | 457 | */ |
| 459 | 458 | export function convertAI21Messages(messages, charName = '', userName = '') { |
| 460 | 459 | if (!Array.isArray(messages)) { |
| 461 | 460 | return []; |
| 462 | 461 | } |
| @@ -528,7 +527,7 @@ function convertAI21Messages(messages, charName = '', userName = '') { | ||
| 528 | 527 | * @param {string} charName Character name |
| 529 | 528 | * @param {string} userName User name |
| 530 | 529 | */ |
| 531 | 530 | export function convertMistralMessages(messages, charName = '', userName = '') { |
| 532 | 531 | if (!Array.isArray(messages)) { |
| 533 | 532 | return []; |
| 534 | 533 | } |
| @@ -612,7 +611,7 @@ function convertMistralMessages(messages, charName = '', userName = '') { | ||
| 612 | 611 | * @param {boolean} strict Enable strict mode: only allow one system message at the start, force user first message |
| 613 | 612 | * @returns {any[]} Merged messages |
| 614 | 613 | */ |
| 615 | 614 | export function mergeMessages(messages, charName, userName, strict) { |
| 616 | 615 | let mergedMessages = []; |
| 617 | 616 | |
| 618 | 617 | // Remove names from the messages |
| @@ -686,7 +685,7 @@ function mergeMessages(messages, charName, userName, strict) { | ||
| 686 | 685 | * @param {object[]} messages Array of messages |
| 687 | 686 | * @returns {string} Prompt for Text Completion API |
| 688 | 687 | */ |
| 689 | 688 | export function convertTextCompletionPrompt(messages) { |
| 690 | 689 | if (typeof messages === 'string') { |
| 691 | 690 | return messages; |
| 692 | 691 | } |
| @@ -705,14 +704,3 @@ function convertTextCompletionPrompt(messages) { | ||
| 705 | 704 | }); |
| 706 | 705 | return messageStrings.join('\n') + '\nassistant:'; |
| 707 | 706 | } |
| 708 | - | |
| 709 | -module.exports = { | |
| 710 | - convertClaudePrompt, | |
| 711 | - convertClaudeMessages, | |
| 712 | - convertGooglePrompt, | |
| 713 | - convertTextCompletionPrompt, | |
| 714 | - convertCohereMessages, | |
| 715 | - convertMistralMessages, | |
| 716 | - convertAI21Messages, | |
| 717 | - mergeMessages, | |
| 718 | -}; | |
| @@ -1,7 +1,8 @@ | ||
| 1 | 1 | constimport httpprocess =from require('node:httpprocess'); |
| 2 | 2 | constimport httpshttp =from require('node:httpshttp'); |
| 3 | - | |
| 3 | +import https from 'node:https'; | |
| 4 | 4 | constimport { isValidUrl, colorProxyAgent } =from require('./util.jsproxy-agent'); |
| 5 | +import { isValidUrl, color } from './util.js'; | |
| 5 | 6 | |
| 6 | 7 | const LOG_HEADER = '[Request Proxy]'; |
| 7 | 8 | |
| @@ -13,10 +14,8 @@ const LOG_HEADER = '[Request Proxy]'; | ||
| 13 | 14 | * @property {string} url Proxy URL. |
| 14 | 15 | * @property {string[]} bypass List of URLs to bypass proxy. |
| 15 | 16 | */ |
| 16 | 17 | export default function initRequestProxy({ enabled, url, bypass }) { |
| 17 | 18 | try { |
| 18 | - const { ProxyAgent } = require('proxy-agent'); | |
| 19 | - | |
| 20 | 19 | // No proxy is enabled, so return |
| 21 | 20 | if (!enabled) { |
| 22 | 21 | return; |
| @@ -36,7 +35,6 @@ function initRequestProxy({ enabled, url, bypass }) { | ||
| 36 | 35 | // Reference: https://github.com/Rob--W/proxy-from-env |
| 37 | 36 | process.env.all_proxy = url; |
| 38 | 37 | |
| 39 | - | |
| 40 | 38 | if (Array.isArray(bypass) && bypass.length > 0) { |
| 41 | 39 | process.env.no_proxy = bypass.join(','); |
| 42 | 40 | } |
| @@ -52,5 +50,3 @@ function initRequestProxy({ enabled, url, bypass }) { | ||
| 52 | 50 | console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error); |
| 53 | 51 | } |
| 54 | 52 | } |
| 55 | - | |
| 56 | -module.exports = initRequestProxy; | |
| @@ -1,7 +1,10 @@ | ||
| 1 | -import { pipeline, env, RawImage, Pipeline } from 'sillytavern-transformers'; | |
| 1 | +import path from 'node:path'; | |
| 2 | +import fs from 'node:fs'; | |
| 3 | +import process from 'node:process'; | |
| 4 | +import { Buffer } from 'node:buffer'; | |
| 5 | + | |
| 6 | +import { pipeline, env, RawImage } from 'sillytavern-transformers'; | |
| 2 | 7 | import { getConfigValue } from './util.js'; |
| 3 | -import path from 'path'; | |
| 4 | -import fs from 'fs'; | |
| 5 | 8 | |
| 6 | 9 | configureTransformers(); |
| 7 | 10 | |
| @@ -50,7 +53,7 @@ const tasks = { | ||
| 50 | 53 | * @param {string} image Base64-encoded image |
| 51 | 54 | * @returns {Promise<RawImage|null>} Object representing the image |
| 52 | 55 | */ |
| 53 | 56 | export async function getRawImage(image) { |
| 54 | 57 | try { |
| 55 | 58 | const buffer = Buffer.from(image, 'base64'); |
| 56 | 59 | const byteArray = new Uint8Array(buffer); |
| @@ -114,9 +117,9 @@ async function migrateCacheToDataDir() { | ||
| 114 | 117 | * Gets the transformers.js pipeline for a given task. |
| 115 | 118 | * @param {import('sillytavern-transformers').PipelineType} task The task to get the pipeline for |
| 116 | 119 | * @param {string} forceModel The model to use for the pipeline, if any |
| 117 | 120 | * @returns {Promise<import('sillytavern-transformers').Pipeline>} Pipeline forThe thetransformers.js taskpipeline |
| 118 | 121 | */ |
| 119 | 122 | export async function getPipeline(task, forceModel = '') { |
| 120 | 123 | await migrateCacheToDataDir(); |
| 121 | 124 | |
| 122 | 125 | if (tasks[task].pipeline) { |
| @@ -134,10 +137,11 @@ async function getPipeline(task, forceModel = '') { | ||
| 134 | 137 | const instance = await pipeline(task, model, { cache_dir: cacheDir, quantized: tasks[task].quantized ?? true, local_files_only: localOnly }); |
| 135 | 138 | tasks[task].pipeline = instance; |
| 136 | 139 | tasks[task].currentModel = model; |
| 140 | + // @ts-ignore | |
| 137 | 141 | return instance; |
| 138 | 142 | } |
| 139 | 143 | |
| 140 | 144 | export default { |
| 141 | - getPipeline, | |
| 142 | 145 | getRawImage, |
| 146 | + getPipeline, | |
| 143 | 147 | }; |
| @@ -1,22 +1,25 @@ | ||
| 1 | 1 | // Native Node Modules |
| 2 | 2 | constimport path =from require('node:path'); |
| 3 | 3 | constimport fs =from require('node:fs'); |
| 4 | 4 | constimport crypto =from require('node:crypto'); |
| 5 | 5 | constimport os =from require('node:os'); |
| 6 | +import process from 'node:process'; | |
| 7 | +import { Buffer } from 'node:buffer'; | |
| 6 | 8 | |
| 7 | 9 | // Express and other dependencies |
| 8 | 10 | constimport storage =from require('node-persist'); |
| 9 | 11 | constimport express =from require('express'); |
| 10 | 12 | constimport mime =from require('mime-types'); |
| 11 | 13 | constimport archiver =from require('archiver'); |
| 12 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 14 | +import _ from 'lodash'; | |
| 13 | -const _ = require('lodash'); | |
| 15 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 14 | 16 | |
| 15 | 17 | constimport { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE } =from require('./constants.js'); |
| 16 | 18 | constimport { getConfigValue, color, delay, setConfigValue, generateTimestamp } =from require('./util.js'); |
| 17 | 19 | constimport { readSecret, writeSecret } =from require('./endpoints/secrets.js'); |
| 18 | - | |
| 20 | +import { getContentOfType } from './endpoints/content-manager.js'; | |
| 19 | -const KEY_PREFIX = 'user:'; | |
| 21 | + | |
| 22 | +export const KEY_PREFIX = 'user:'; | |
| 20 | 23 | const AVATAR_PREFIX = 'avatar:'; |
| 21 | 24 | const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false); |
| 22 | 25 | const AUTHELIA_AUTH = getConfigValue('autheliaAuth', false); |
| @@ -92,9 +95,9 @@ const STORAGE_KEYS = { | ||
| 92 | 95 | |
| 93 | 96 | /** |
| 94 | 97 | * Ensures that the content directories exist. |
| 95 | 98 | * @returns {Promise<import('./users.js').UserDirectoryList[]>} - The list of user directories |
| 96 | 99 | */ |
| 97 | 100 | export async function ensurePublicDirectoriesExist() { |
| 98 | 101 | for (const dir of Object.values(PUBLIC_DIRECTORIES)) { |
| 99 | 102 | if (!fs.existsSync(dir)) { |
| 100 | 103 | fs.mkdirSync(dir, { recursive: true }); |
| @@ -115,9 +118,9 @@ async function ensurePublicDirectoriesExist() { | ||
| 115 | 118 | |
| 116 | 119 | /** |
| 117 | 120 | * Gets a list of all user directories. |
| 118 | 121 | * @returns {Promise<import('./users.js').UserDirectoryList[]>} - The list of user directories |
| 119 | 122 | */ |
| 120 | 123 | export async function getUserDirectoriesList() { |
| 121 | 124 | const userHandles = await getAllUserHandles(); |
| 122 | 125 | const directoriesList = userHandles.map(handle => getUserDirectories(handle)); |
| 123 | 126 | return directoriesList; |
| @@ -126,7 +129,7 @@ async function getUserDirectoriesList() { | ||
| 126 | 129 | /** |
| 127 | 130 | * Perform migration from the old user data format to the new one. |
| 128 | 131 | */ |
| 129 | 132 | export async function migrateUserData() { |
| 130 | 133 | const publicDirectory = path.join(process.cwd(), 'public'); |
| 131 | 134 | |
| 132 | 135 | // No need to migrate if the characters directory doesn't exists |
| @@ -328,14 +331,13 @@ async function migrateUserData() { | ||
| 328 | 331 | console.log(color.green('Migration completed!')); |
| 329 | 332 | } |
| 330 | 333 | |
| 331 | 334 | export async function migrateSystemPrompts() { |
| 332 | 335 | /** |
| 333 | 336 | * Gets the default system prompts. |
| 334 | 337 | * @returns {Promise<any[]>} - The list of default system prompts |
| 335 | 338 | */ |
| 336 | 339 | async function getDefaultSystemPrompts() { |
| 337 | 340 | try { |
| 338 | - const { getContentOfType } = await import('./endpoints/content-manager.js'); | |
| 339 | 341 | return getContentOfType('sysprompt', 'json'); |
| 340 | 342 | } catch { |
| 341 | 343 | return []; |
| @@ -391,7 +393,7 @@ async function migrateSystemPrompts() { | ||
| 391 | 393 | * @param {string} handle User handle |
| 392 | 394 | * @returns {string} The key for the user storage |
| 393 | 395 | */ |
| 394 | 396 | export function toKey(handle) { |
| 395 | 397 | return `${KEY_PREFIX}${handle}`; |
| 396 | 398 | } |
| 397 | 399 | |
| @@ -400,7 +402,7 @@ function toKey(handle) { | ||
| 400 | 402 | * @param {string} handle User handle |
| 401 | 403 | * @returns {string} The key for the avatar storage |
| 402 | 404 | */ |
| 403 | 405 | export function toAvatarKey(handle) { |
| 404 | 406 | return `${AVATAR_PREFIX}${handle}`; |
| 405 | 407 | } |
| 406 | 408 | |
| @@ -409,7 +411,7 @@ function toAvatarKey(handle) { | ||
| 409 | 411 | * @param {string} dataRoot The root directory for user data |
| 410 | 412 | * @returns {Promise<void>} |
| 411 | 413 | */ |
| 412 | 414 | export async function initUserStorage(dataRoot) { |
| 413 | 415 | global.DATA_ROOT = dataRoot; |
| 414 | 416 | console.log('Using data root:', color.green(global.DATA_ROOT)); |
| 415 | 417 | console.log(); |
| @@ -430,7 +432,7 @@ async function initUserStorage(dataRoot) { | ||
| 430 | 432 | * Get the cookie secret from the config. If it doesn't exist, generate a new one. |
| 431 | 433 | * @returns {string} The cookie secret |
| 432 | 434 | */ |
| 433 | 435 | export function getCookieSecret() { |
| 434 | 436 | let secret = getConfigValue(STORAGE_KEYS.cookieSecret); |
| 435 | 437 | |
| 436 | 438 | if (!secret) { |
| @@ -446,7 +448,7 @@ function getCookieSecret() { | ||
| 446 | 448 | * Generates a random password salt. |
| 447 | 449 | * @returns {string} The password salt |
| 448 | 450 | */ |
| 449 | 451 | export function getPasswordSalt() { |
| 450 | 452 | return crypto.randomBytes(16).toString('base64'); |
| 451 | 453 | } |
| 452 | 454 | |
| @@ -454,7 +456,7 @@ function getPasswordSalt() { | ||
| 454 | 456 | * Get the session name for the current server. |
| 455 | 457 | * @returns {string} The session name |
| 456 | 458 | */ |
| 457 | 459 | export function getCookieSessionName() { |
| 458 | 460 | // Get server hostname and hash it to generate a session suffix |
| 459 | 461 | const suffix = crypto.createHash('sha256').update(os.hostname()).digest('hex').slice(0, 8); |
| 460 | 462 | return `session-${suffix}`; |
| @@ -466,7 +468,7 @@ function getCookieSessionName() { | ||
| 466 | 468 | * @param {string} salt Salt to use for hashing |
| 467 | 469 | * @returns {string} Hashed password |
| 468 | 470 | */ |
| 469 | 471 | export function getPasswordHash(password, salt) { |
| 470 | 472 | return crypto.scryptSync(password.normalize(), salt, 64).toString('base64'); |
| 471 | 473 | } |
| 472 | 474 | |
| @@ -475,7 +477,7 @@ function getPasswordHash(password, salt) { | ||
| 475 | 477 | * @param {import('express').Request} [request] HTTP request object |
| 476 | 478 | * @returns {string} The CSRF secret |
| 477 | 479 | */ |
| 478 | 480 | export function getCsrfSecret(request) { |
| 479 | 481 | if (!request || !request.user) { |
| 480 | 482 | return ANON_CSRF_SECRET; |
| 481 | 483 | } |
| @@ -494,7 +496,7 @@ function getCsrfSecret(request) { | ||
| 494 | 496 | * Gets a list of all user handles. |
| 495 | 497 | * @returns {Promise<string[]>} - The list of user handles |
| 496 | 498 | */ |
| 497 | 499 | export async function getAllUserHandles() { |
| 498 | 500 | const keys = await storage.keys(x => x.key.startsWith(KEY_PREFIX)); |
| 499 | 501 | const handles = keys.map(x => x.replace(KEY_PREFIX, '')); |
| 500 | 502 | return handles; |
| @@ -505,7 +507,7 @@ async function getAllUserHandles() { | ||
| 505 | 507 | * @param {string} handle User handle |
| 506 | 508 | * @returns {UserDirectoryList} User directories |
| 507 | 509 | */ |
| 508 | 510 | export function getUserDirectories(handle) { |
| 509 | 511 | if (DIRECTORIES_CACHE.has(handle)) { |
| 510 | 512 | const cache = DIRECTORIES_CACHE.get(handle); |
| 511 | 513 | if (cache) { |
| @@ -526,7 +528,7 @@ function getUserDirectories(handle) { | ||
| 526 | 528 | * @param {string} handle User handle |
| 527 | 529 | * @returns {Promise<string>} User avatar URL |
| 528 | 530 | */ |
| 529 | 531 | export async function getUserAvatar(handle) { |
| 530 | 532 | try { |
| 531 | 533 | // Check if the user has a custom avatar |
| 532 | 534 | const avatarKey = toAvatarKey(handle); |
| @@ -563,7 +565,7 @@ async function getUserAvatar(handle) { | ||
| 563 | 565 | * @param {import('express').Request} request Request object |
| 564 | 566 | * @returns {boolean} Whether the user should be redirected to the login page |
| 565 | 567 | */ |
| 566 | 568 | export function shouldRedirectToLogin(request) { |
| 567 | 569 | return ENABLE_ACCOUNTS && !request.user; |
| 568 | 570 | } |
| 569 | 571 | |
| @@ -574,7 +576,7 @@ function shouldRedirectToLogin(request) { | ||
| 574 | 576 | * @param {boolean} basicAuthMode If Basic auth mode is enabled |
| 575 | 577 | * @returns {Promise<boolean>} Whether auto-login was performed |
| 576 | 578 | */ |
| 577 | 579 | export async function tryAutoLogin(request, basicAuthMode) { |
| 578 | 580 | if (!ENABLE_ACCOUNTS || request.user || !request.session) { |
| 579 | 581 | return false; |
| 580 | 582 | } |
| @@ -693,7 +695,7 @@ async function basicUserLogin(request) { | ||
| 693 | 695 | * @param {import('express').Response} response Response object |
| 694 | 696 | * @param {import('express').NextFunction} next Next function |
| 695 | 697 | */ |
| 696 | 698 | export async function setUserDataMiddleware(request, response, next) { |
| 697 | 699 | // If user accounts are disabled, use the default user |
| 698 | 700 | if (!ENABLE_ACCOUNTS) { |
| 699 | 701 | const handle = DEFAULT_USER.handle; |
| @@ -751,7 +753,7 @@ async function setUserDataMiddleware(request, response, next) { | ||
| 751 | 753 | * @param {import('express').Response} response Response object |
| 752 | 754 | * @param {import('express').NextFunction} next Next function |
| 753 | 755 | */ |
| 754 | 756 | export function requireLoginMiddleware(request, response, next) { |
| 755 | 757 | if (!request.user) { |
| 756 | 758 | return response.sendStatus(403); |
| 757 | 759 | } |
| @@ -787,7 +789,7 @@ function createRouteHandler(directoryFn) { | ||
| 787 | 789 | * @param {import('express').NextFunction} next Next function |
| 788 | 790 | * @returns {any} |
| 789 | 791 | */ |
| 790 | 792 | export function requireAdminMiddleware(request, response, next) { |
| 791 | 793 | if (!request.user) { |
| 792 | 794 | return response.sendStatus(403); |
| 793 | 795 | } |
| @@ -806,7 +808,7 @@ function requireAdminMiddleware(request, response, next) { | ||
| 806 | 808 | * @param {import('express').Response} response Express response object to write to |
| 807 | 809 | * @returns {Promise<void>} Promise that resolves when the archive is created |
| 808 | 810 | */ |
| 809 | 811 | export async function createBackupArchive(handle, response) { |
| 810 | 812 | const directories = getUserDirectories(handle); |
| 811 | 813 | |
| 812 | 814 | console.log('Backup requested for', handle); |
| @@ -855,7 +857,7 @@ async function getAllUsers() { | ||
| 855 | 857 | * Gets all of the enabled users. |
| 856 | 858 | * @returns {Promise<User[]>} |
| 857 | 859 | */ |
| 858 | 860 | export async function getAllEnabledUsers() { |
| 859 | 861 | const users = await getAllUsers(); |
| 860 | 862 | return users.filter(x => x.enabled); |
| 861 | 863 | } |
| @@ -863,7 +865,7 @@ async function getAllEnabledUsers() { | ||
| 863 | 865 | /** |
| 864 | 866 | * Express router for serving files from the user's directories. |
| 865 | 867 | */ |
| 866 | 868 | export const router = express.Router(); |
| 867 | 869 | router.use('/backgrounds/*', createRouteHandler(req => req.user.directories.backgrounds)); |
| 868 | 870 | router.use('/characters/*', createRouteHandler(req => req.user.directories.characters)); |
| 869 | 871 | router.use('/User%20Avatars/*', createRouteHandler(req => req.user.directories.avatars)); |
| @@ -871,31 +873,3 @@ router.use('/assets/*', createRouteHandler(req => req.user.directories.assets)); | ||
| 871 | 873 | router.use('/user/images/*', createRouteHandler(req => req.user.directories.userImages)); |
| 872 | 874 | router.use('/user/files/*', createRouteHandler(req => req.user.directories.files)); |
| 873 | 875 | router.use('/scripts/extensions/third-party/*', createRouteHandler(req => req.user.directories.extensions)); |
| 874 | - | |
| 875 | -module.exports = { | |
| 876 | - KEY_PREFIX, | |
| 877 | - toKey, | |
| 878 | - toAvatarKey, | |
| 879 | - initUserStorage, | |
| 880 | - ensurePublicDirectoriesExist, | |
| 881 | - getUserDirectoriesList, | |
| 882 | - getAllUserHandles, | |
| 883 | - getUserDirectories, | |
| 884 | - setUserDataMiddleware, | |
| 885 | - requireLoginMiddleware, | |
| 886 | - requireAdminMiddleware, | |
| 887 | - migrateUserData, | |
| 888 | - migrateSystemPrompts, | |
| 889 | - getPasswordSalt, | |
| 890 | - getPasswordHash, | |
| 891 | - getCsrfSecret, | |
| 892 | - getCookieSecret, | |
| 893 | - getCookieSessionName, | |
| 894 | - getUserAvatar, | |
| 895 | - shouldRedirectToLogin, | |
| 896 | - createBackupArchive, | |
| 897 | - tryAutoLogin, | |
| 898 | - getAllUsers, | |
| 899 | - getAllEnabledUsers, | |
| 900 | - router, | |
| 901 | -}; | |
| @@ -1,13 +1,18 @@ | ||
| 1 | 1 | constimport path =from require('node:path'); |
| 2 | 2 | constimport fs =from require('node:fs'); |
| 3 | -const commandExistsSync = require('command-exists').sync; | |
| 3 | +import http2 from 'node:http2'; | |
| 4 | -const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 4 | +import process from 'node:process'; | |
| 5 | -const _ = require('lodash'); | |
| 5 | +import { Readable } from 'node:stream'; | |
| 6 | -const yauzl = require('yauzl'); | |
| 6 | +import { createRequire } from 'node:module'; | |
| 7 | -const mime = require('mime-types'); | |
| 7 | +import { Buffer } from 'node:buffer'; | |
| 8 | -const yaml = require('yaml'); | |
| 8 | + | |
| 9 | -const { default: simpleGit } = require('simple-git'); | |
| 9 | +import yaml from 'yaml'; | |
| 10 | 10 | constimport { Readablesync as commandExistsSync } =from require('streamcommand-exists'); |
| 11 | +import { sync as writeFileAtomicSync } from 'write-file-atomic'; | |
| 12 | +import _ from 'lodash'; | |
| 13 | +import yauzl from 'yauzl'; | |
| 14 | +import mime from 'mime-types'; | |
| 15 | +import { default as simpleGit } from 'simple-git'; | |
| 11 | 16 | |
| 12 | 17 | /** |
| 13 | 18 | * Parsed config object. |
| @@ -18,7 +23,7 @@ let CACHED_CONFIG = null; | ||
| 18 | 23 | * Returns the config object from the config.yaml file. |
| 19 | 24 | * @returns {object} Config object |
| 20 | 25 | */ |
| 21 | 26 | export function getConfig() { |
| 22 | 27 | if (CACHED_CONFIG) { |
| 23 | 28 | return CACHED_CONFIG; |
| 24 | 29 | } |
| @@ -46,7 +51,7 @@ function getConfig() { | ||
| 46 | 51 | * @param {any} defaultValue - Default value to return if the key is not found |
| 47 | 52 | * @returns {any} Value for the given key |
| 48 | 53 | */ |
| 49 | 54 | export function getConfigValue(key, defaultValue = null) { |
| 50 | 55 | const config = getConfig(); |
| 51 | 56 | return _.get(config, key, defaultValue); |
| 52 | 57 | } |
| @@ -56,7 +61,7 @@ function getConfigValue(key, defaultValue = null) { | ||
| 56 | 61 | * @param {string} key Key to set |
| 57 | 62 | * @param {any} value Value to set |
| 58 | 63 | */ |
| 59 | 64 | export function setConfigValue(key, value) { |
| 60 | 65 | // Reset cache so that the next getConfig call will read the updated config file |
| 61 | 66 | CACHED_CONFIG = null; |
| 62 | 67 | const config = getConfig(); |
| @@ -69,7 +74,7 @@ function setConfigValue(key, value) { | ||
| 69 | 74 | * @param {string} auth username:password |
| 70 | 75 | * @returns {string} Basic Auth header value |
| 71 | 76 | */ |
| 72 | 77 | export function getBasicAuthHeader(auth) { |
| 73 | 78 | const encoded = Buffer.from(`${auth}`).toString('base64'); |
| 74 | 79 | return `Basic ${encoded}`; |
| 75 | 80 | } |
| @@ -79,7 +84,7 @@ function getBasicAuthHeader(auth) { | ||
| 79 | 84 | * Also returns the agent string for the Horde API. |
| 80 | 85 | * @returns {Promise<{agent: string, pkgVersion: string, gitRevision: string | null, gitBranch: string | null, commitDate: string | null, isLatest: boolean}>} Version info object |
| 81 | 86 | */ |
| 82 | 87 | export async function getVersion() { |
| 83 | 88 | let pkgVersion = 'UNKNOWN'; |
| 84 | 89 | let gitRevision = null; |
| 85 | 90 | let gitBranch = null; |
| @@ -87,9 +92,10 @@ async function getVersion() { | ||
| 87 | 92 | let isLatest = true; |
| 88 | 93 | |
| 89 | 94 | try { |
| 95 | + const require = createRequire(import.meta.url); | |
| 90 | 96 | const pkgJson = require(path.join(process.cwd(), './package.json')); |
| 91 | 97 | pkgVersion = pkgJson.version; |
| 92 | 98 | if (!process['pkg'] && commandExistsSync('git')) { |
| 93 | 99 | const git = simpleGit(); |
| 94 | 100 | const cwd = process.cwd(); |
| 95 | 101 | gitRevision = await git.cwd(cwd).revparse(['--short', 'HEAD']); |
| @@ -117,7 +123,7 @@ async function getVersion() { | ||
| 117 | 123 | * @param {number} ms Milliseconds to wait |
| 118 | 124 | * @returns {Promise<void>} Promise that resolves after the given amount of milliseconds |
| 119 | 125 | */ |
| 120 | 126 | export function delay(ms) { |
| 121 | 127 | return new Promise(resolve => setTimeout(resolve, ms)); |
| 122 | 128 | } |
| 123 | 129 | |
| @@ -127,7 +133,7 @@ function delay(ms) { | ||
| 127 | 133 | * @returns {string} Random hex string |
| 128 | 134 | * @example getHexString(8) // 'a1b2c3d4' |
| 129 | 135 | */ |
| 130 | 136 | export function getHexString(length) { |
| 131 | 137 | const chars = '0123456789abcdef'; |
| 132 | 138 | let result = ''; |
| 133 | 139 | for (let i = 0; i < length; i++) { |
| @@ -142,7 +148,7 @@ function getHexString(length) { | ||
| 142 | 148 | * @param {string} fileExtension File extension to look for |
| 143 | 149 | * @returns {Promise<Buffer|null>} Buffer containing the extracted file. Null if the file was not found. |
| 144 | 150 | */ |
| 145 | 151 | export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) { |
| 146 | 152 | return await new Promise((resolve, reject) => yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => { |
| 147 | 153 | if (err) { |
| 148 | 154 | reject(err); |
| @@ -181,7 +187,7 @@ async function extractFileFromZipBuffer(archiveBuffer, fileExtension) { | ||
| 181 | 187 | * @param {string} zipFilePath Path to the ZIP archive |
| 182 | 188 | * @returns {Promise<[string, Buffer][]>} Array of image buffers |
| 183 | 189 | */ |
| 184 | 190 | export async function getImageBuffers(zipFilePath) { |
| 185 | 191 | return new Promise((resolve, reject) => { |
| 186 | 192 | // Check if the zip file exists |
| 187 | 193 | if (!fs.existsSync(zipFilePath)) { |
| @@ -237,7 +243,7 @@ async function getImageBuffers(zipFilePath) { | ||
| 237 | 243 | * @param {any} readableStream Readable stream to read from |
| 238 | 244 | * @returns {Promise<Buffer[]>} Array of chunks |
| 239 | 245 | */ |
| 240 | 246 | export async function readAllChunks(readableStream) { |
| 241 | 247 | return new Promise((resolve, reject) => { |
| 242 | 248 | // Consume the readable stream |
| 243 | 249 | const chunks = []; |
| @@ -261,7 +267,7 @@ function isObject(item) { | ||
| 261 | 267 | return (item && typeof item === 'object' && !Array.isArray(item)); |
| 262 | 268 | } |
| 263 | 269 | |
| 264 | 270 | export function deepMerge(target, source) { |
| 265 | 271 | let output = Object.assign({}, target); |
| 266 | 272 | if (isObject(target) && isObject(source)) { |
| 267 | 273 | Object.keys(source).forEach(key => { |
| @@ -278,7 +284,7 @@ function deepMerge(target, source) { | ||
| 278 | 284 | return output; |
| 279 | 285 | } |
| 280 | 286 | |
| 281 | 287 | export const color = { |
| 282 | 288 | byNum: (mess, fgNum) => { |
| 283 | 289 | mess = mess || ''; |
| 284 | 290 | fgNum = fgNum === undefined ? 31 : fgNum; |
| @@ -298,7 +304,7 @@ const color = { | ||
| 298 | 304 | * Gets a random UUIDv4 string. |
| 299 | 305 | * @returns {string} A UUIDv4 string |
| 300 | 306 | */ |
| 301 | 307 | export function uuidv4() { |
| 302 | 308 | if ('crypto' in global && 'randomUUID' in global.crypto) { |
| 303 | 309 | return global.crypto.randomUUID(); |
| 304 | 310 | } |
| @@ -309,7 +315,7 @@ function uuidv4() { | ||
| 309 | 315 | }); |
| 310 | 316 | } |
| 311 | 317 | |
| 312 | 318 | export function humanizedISO8601DateTime(date) { |
| 313 | 319 | let baseDate = typeof date === 'number' ? new Date(date) : new Date(); |
| 314 | 320 | let humanYear = baseDate.getFullYear(); |
| 315 | 321 | let humanMonth = (baseDate.getMonth() + 1); |
| @@ -322,7 +328,7 @@ function humanizedISO8601DateTime(date) { | ||
| 322 | 328 | return HumanizedDateTime; |
| 323 | 329 | } |
| 324 | 330 | |
| 325 | 331 | export function tryParse(str) { |
| 326 | 332 | try { |
| 327 | 333 | return JSON.parse(str); |
| 328 | 334 | } catch { |
| @@ -337,7 +343,7 @@ function tryParse(str) { | ||
| 337 | 343 | * @param {string} inputPath The path to be converted. |
| 338 | 344 | * @returns The relative URL path from which the client can access the file. |
| 339 | 345 | */ |
| 340 | 346 | export function clientRelativePath(root, inputPath) { |
| 341 | 347 | if (!inputPath.startsWith(root)) { |
| 342 | 348 | throw new Error('Input path does not start with the root directory'); |
| 343 | 349 | } |
| @@ -350,11 +356,11 @@ function clientRelativePath(root, inputPath) { | ||
| 350 | 356 | * @param {string} filename The file name to remove the extension from. |
| 351 | 357 | * @returns The file name, sans extension |
| 352 | 358 | */ |
| 353 | 359 | export function removeFileExtension(filename) { |
| 354 | 360 | return filename.replace(/\.[^.]+$/, ''); |
| 355 | 361 | } |
| 356 | 362 | |
| 357 | 363 | export function generateTimestamp() { |
| 358 | 364 | const now = new Date(); |
| 359 | 365 | const year = now.getFullYear(); |
| 360 | 366 | const month = String(now.getMonth() + 1).padStart(2, '0'); |
| @@ -371,7 +377,7 @@ function generateTimestamp() { | ||
| 371 | 377 | * @param {string} directory The root directory to remove backups from. |
| 372 | 378 | * @param {string} prefix File prefix to filter backups by. |
| 373 | 379 | */ |
| 374 | 380 | export function removeOldBackups(directory, prefix) { |
| 375 | 381 | const MAX_BACKUPS = Number(getConfigValue('numberOfBackups', 50)); |
| 376 | 382 | |
| 377 | 383 | let files = fs.readdirSync(directory).filter(f => f.startsWith(prefix)); |
| @@ -389,7 +395,7 @@ function removeOldBackups(directory, prefix) { | ||
| 389 | 395 | * @param {'name' | 'date'} sortBy Sort images by name or date |
| 390 | 396 | * @returns {string[]} List of image file names |
| 391 | 397 | */ |
| 392 | 398 | export function getImages(directoryPath, sortBy = 'name') { |
| 393 | 399 | function getSortFunction() { |
| 394 | 400 | switch (sortBy) { |
| 395 | 401 | case 'name': |
| @@ -415,7 +421,7 @@ function getImages(directoryPath, sortBy = 'name') { | ||
| 415 | 421 | * @param {import('node-fetch').Response} from The Fetch API response to pipe from. |
| 416 | 422 | * @param {import('express').Response} to The Express response to pipe to. |
| 417 | 423 | */ |
| 418 | 424 | export function forwardFetchResponse(from, to) { |
| 419 | 425 | let statusCode = from.status; |
| 420 | 426 | let statusText = from.statusText; |
| 421 | 427 | |
| @@ -434,17 +440,22 @@ function forwardFetchResponse(from, to) { | ||
| 434 | 440 | |
| 435 | 441 | to.statusCode = statusCode; |
| 436 | 442 | to.statusMessage = statusText; |
| 437 | - from.body.pipe(to); | |
| 438 | 443 | |
| 439 | - to.socket.on('close', function () { | |
| 444 | + if (from.body && to.socket) { | |
| 440 | - if (from.body instanceof Readable) from.body.destroy(); // Close the remote stream | |
| 445 | + from.body.pipe(to); | |
| 441 | - to.end(); // End the Express response | |
| 446 | + | |
| 442 | - }); | |
| 447 | + to.socket.on('close', function () { | |
| 448 | + if (from.body instanceof Readable) from.body.destroy(); // Close the remote stream | |
| 449 | + to.end(); // End the Express response | |
| 450 | + }); | |
| 443 | 451 | |
| 444 | 452 | from.body.on('end', function () { |
| 445 | 453 | console.log('Streaming request finished'); |
| 454 | + to.end(); | |
| 455 | + }); | |
| 456 | + } else { | |
| 446 | 457 | to.end(); |
| 447 | 458 | }); |
| 448 | 459 | } |
| 449 | 460 | |
| 450 | 461 | /** |
| @@ -457,10 +468,9 @@ function forwardFetchResponse(from, to) { | ||
| 457 | 468 | * @param {object} headers Request headers |
| 458 | 469 | * @returns {Promise<string>} Response body |
| 459 | 470 | */ |
| 460 | 471 | export function makeHttp2Request(endpoint, method, body, headers) { |
| 461 | 472 | return new Promise((resolve, reject) => { |
| 462 | 473 | try { |
| 463 | - const http2 = require('http2'); | |
| 464 | 474 | const url = new URL(endpoint); |
| 465 | 475 | const client = http2.connect(url.origin); |
| 466 | 476 | |
| @@ -511,7 +521,7 @@ function makeHttp2Request(endpoint, method, body, headers) { | ||
| 511 | 521 | * @param {string} yamlString YAML-serialized object |
| 512 | 522 | * @returns |
| 513 | 523 | */ |
| 514 | 524 | export function mergeObjectWithYaml(obj, yamlString) { |
| 515 | 525 | if (!yamlString) { |
| 516 | 526 | return; |
| 517 | 527 | } |
| @@ -540,7 +550,7 @@ function mergeObjectWithYaml(obj, yamlString) { | ||
| 540 | 550 | * @param {string} yamlString YAML-serialized array |
| 541 | 551 | * @returns {void} Nothing |
| 542 | 552 | */ |
| 543 | 553 | export function excludeKeysByYaml(obj, yamlString) { |
| 544 | 554 | if (!yamlString) { |
| 545 | 555 | return; |
| 546 | 556 | } |
| @@ -569,14 +579,14 @@ function excludeKeysByYaml(obj, yamlString) { | ||
| 569 | 579 | * @param {string} str Input string |
| 570 | 580 | * @returns {string} Trimmed string |
| 571 | 581 | */ |
| 572 | 582 | export function trimV1(str) { |
| 573 | 583 | return String(str ?? '').replace(/\/$/, '').replace(/\/v1$/, ''); |
| 574 | 584 | } |
| 575 | 585 | |
| 576 | 586 | /** |
| 577 | 587 | * Simple TTL memory cache. |
| 578 | 588 | */ |
| 579 | 589 | export class Cache { |
| 580 | 590 | /** |
| 581 | 591 | * @param {number} ttl Time to live in milliseconds |
| 582 | 592 | */ |
| @@ -633,7 +643,7 @@ class Cache { | ||
| 633 | 643 | * @param {string} text Text with color formatting |
| 634 | 644 | * @returns {string} Text without color formatting |
| 635 | 645 | */ |
| 636 | 646 | export function removeColorFormatting(text) { |
| 637 | 647 | // ANSI escape codes for colors are usually in the format \x1b[<codes>m |
| 638 | 648 | return text.replace(/\x1b\[\d{1,2}(;\d{1,2})*m/g, ''); |
| 639 | 649 | } |
| @@ -643,7 +653,7 @@ function removeColorFormatting(text) { | ||
| 643 | 653 | * @param {number} n Number of times to repeat the separator |
| 644 | 654 | * @returns {string} Separator string |
| 645 | 655 | */ |
| 646 | 656 | export function getSeparator(n) { |
| 647 | 657 | return '='.repeat(n); |
| 648 | 658 | } |
| 649 | 659 | |
| @@ -652,7 +662,7 @@ function getSeparator(n) { | ||
| 652 | 662 | * @param {string} url String to check |
| 653 | 663 | * @returns {boolean} If the URL is valid |
| 654 | 664 | */ |
| 655 | 665 | export function isValidUrl(url) { |
| 656 | 666 | try { |
| 657 | 667 | new URL(url); |
| 658 | 668 | return true; |
| @@ -660,35 +670,3 @@ function isValidUrl(url) { | ||
| 660 | 670 | return false; |
| 661 | 671 | } |
| 662 | 672 | } |
| 663 | - | |
| 664 | -module.exports = { | |
| 665 | - getConfig, | |
| 666 | - getConfigValue, | |
| 667 | - setConfigValue, | |
| 668 | - getVersion, | |
| 669 | - getBasicAuthHeader, | |
| 670 | - extractFileFromZipBuffer, | |
| 671 | - getImageBuffers, | |
| 672 | - readAllChunks, | |
| 673 | - delay, | |
| 674 | - deepMerge, | |
| 675 | - color, | |
| 676 | - uuidv4, | |
| 677 | - humanizedISO8601DateTime, | |
| 678 | - tryParse, | |
| 679 | - clientRelativePath, | |
| 680 | - removeFileExtension, | |
| 681 | - generateTimestamp, | |
| 682 | - removeOldBackups, | |
| 683 | - getImages, | |
| 684 | - forwardFetchResponse, | |
| 685 | - getHexString, | |
| 686 | - mergeObjectWithYaml, | |
| 687 | - excludeKeysByYaml, | |
| 688 | - trimV1, | |
| 689 | - Cache, | |
| 690 | - makeHttp2Request, | |
| 691 | - removeColorFormatting, | |
| 692 | - getSeparator, | |
| 693 | - isValidUrl, | |
| 694 | -}; | |
| @@ -5,7 +5,7 @@ | ||
| 5 | 5 | * |
| 6 | 6 | * @link https://github.com/malfoyslastname/character-card-spec-v2 |
| 7 | 7 | */ |
| 8 | 8 | export class TavernCardValidator { |
| 9 | 9 | /** |
| 10 | 10 | * @type {string|null} |
| 11 | 11 | */ |
| @@ -167,5 +167,3 @@ class TavernCardValidator { | ||
| 167 | 167 | return true; |
| 168 | 168 | } |
| 169 | 169 | } |
| 170 | - | |
| 171 | -module.exports = { TavernCardValidator }; | |
| @@ -1,15 +1,15 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport { SECRET_KEYS, readSecret } =from require('../endpoints/secrets.js'); |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Gets the vector for the given text batch from an OpenAI compatible endpoint. |
| 6 | 6 | * @param {string[]} texts - The array of texts to get the vector for |
| 7 | 7 | * @param {boolean} isQuery - If the text is a query for embedding search |
| 8 | 8 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 9 | 9 | * @param {string} model - The model to use for the embedding |
| 10 | 10 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 11 | 11 | */ |
| 12 | 12 | export async function getCohereBatchVector(texts, isQuery, directories, model) { |
| 13 | 13 | const key = readSecret(directories, SECRET_KEYS.COHERE); |
| 14 | 14 | |
| 15 | 15 | if (!key) { |
| @@ -38,6 +38,7 @@ async function getCohereBatchVector(texts, isQuery, directories, model) { | ||
| 38 | 38 | throw new Error('API request failed'); |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | + /** @type {any} */ | |
| 41 | 42 | const data = await response.json(); |
| 42 | 43 | if (!Array.isArray(data?.embeddings?.float)) { |
| 43 | 44 | console.log('API response was not an array'); |
| @@ -51,16 +52,12 @@ async function getCohereBatchVector(texts, isQuery, directories, model) { | ||
| 51 | 52 | * Gets the vector for the given text from an OpenAI compatible endpoint. |
| 52 | 53 | * @param {string} text - The text to get the vector for |
| 53 | 54 | * @param {boolean} isQuery - If the text is a query for embedding search |
| 54 | 55 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 55 | 56 | * @param {string} model - The model to use for the embedding |
| 56 | 57 | * @returns {Promise<number[]>} - The vector for the text |
| 57 | 58 | */ |
| 58 | 59 | export async function getCohereVector(text, isQuery, directories, model) { |
| 59 | 60 | const vectors = await getCohereBatchVector([text], isQuery, directories, model); |
| 60 | 61 | return vectors[0]; |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | -module.exports = { | |
| 64 | - getCohereBatchVector, | |
| 65 | - getCohereVector, | |
| 66 | -}; | |
| @@ -1,3 +1,4 @@ | ||
| 1 | +import { getPipeline } from '../transformers.mjs'; | |
| 1 | 2 | const TASK = 'feature-extraction'; |
| 2 | 3 | |
| 3 | 4 | /** |
| @@ -5,9 +6,8 @@ const TASK = 'feature-extraction'; | ||
| 5 | 6 | * @param {string} text - The text to vectorize |
| 6 | 7 | * @returns {Promise<number[]>} - The vectorized text in form of an array of numbers |
| 7 | 8 | */ |
| 8 | 9 | export async function getTransformersVector(text) { |
| 9 | 10 | const modulepipe = await importgetPipeline('../transformers.mjs'TASK); |
| 10 | - const pipe = await module.default.getPipeline(TASK); | |
| 11 | 11 | const result = await pipe(text, { pooling: 'mean', normalize: true }); |
| 12 | 12 | const vector = Array.from(result.data); |
| 13 | 13 | return vector; |
| @@ -18,15 +18,10 @@ async function getTransformersVector(text) { | ||
| 18 | 18 | * @param {string[]} texts - The texts to vectorize |
| 19 | 19 | * @returns {Promise<number[][]>} - The vectorized texts in form of an array of arrays of numbers |
| 20 | 20 | */ |
| 21 | 21 | export async function getTransformersBatchVector(texts) { |
| 22 | 22 | const result = []; |
| 23 | 23 | for (const text of texts) { |
| 24 | 24 | result.push(await getTransformersVector(text)); |
| 25 | 25 | } |
| 26 | 26 | return result; |
| 27 | 27 | } |
| 28 | - | |
| 29 | -module.exports = { | |
| 30 | - getTransformersVector, | |
| 31 | - getTransformersBatchVector, | |
| 32 | -}; | |
| @@ -1,4 +1,4 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | |
| 3 | 3 | /** |
| 4 | 4 | * Gets the vector for the given text from SillyTavern-extras |
| @@ -7,7 +7,7 @@ const fetch = require('node-fetch').default; | ||
| 7 | 7 | * @param {string} apiKey - The Extras API key, or empty string if API key not enabled |
| 8 | 8 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 9 | 9 | */ |
| 10 | 10 | export async function getExtrasBatchVector(texts, apiUrl, apiKey) { |
| 11 | 11 | return getExtrasVectorImpl(texts, apiUrl, apiKey); |
| 12 | 12 | } |
| 13 | 13 | |
| @@ -18,7 +18,7 @@ async function getExtrasBatchVector(texts, apiUrl, apiKey) { | ||
| 18 | 18 | * @param {string} apiKey - The Extras API key, or empty string if API key not enabled |
| 19 | 19 | * @returns {Promise<number[]>} - The vector for the text |
| 20 | 20 | */ |
| 21 | 21 | export async function getExtrasVector(text, apiUrl, apiKey) { |
| 22 | 22 | return getExtrasVectorImpl(text, apiUrl, apiKey); |
| 23 | 23 | } |
| 24 | 24 | |
| @@ -66,13 +66,9 @@ async function getExtrasVectorImpl(text, apiUrl, apiKey) { | ||
| 66 | 66 | throw new Error('Extras request failed'); |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | + /** @type {any} */ | |
| 69 | 70 | const data = await response.json(); |
| 70 | 71 | const vector = data.embedding; // `embedding`: number[] (one text item), or number[][] (multiple text items). |
| 71 | 72 | |
| 72 | 73 | return vector; |
| 73 | 74 | } |
| 74 | - | |
| 75 | -module.exports = { | |
| 76 | - getExtrasVector, | |
| 77 | - getExtrasBatchVector, | |
| 78 | -}; | |
| @@ -1,15 +1,15 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport { setAdditionalHeadersByType } =from require('../additional-headers.js'); |
| 3 | 3 | constimport { TEXTGEN_TYPES } =from require('../constants.js'); |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * Gets the vector for the given text from LlamaCpp |
| 7 | 7 | * @param {string[]} texts - The array of texts to get the vectors for |
| 8 | 8 | * @param {string} apiUrl - The API URL |
| 9 | 9 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 10 | 10 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 11 | 11 | */ |
| 12 | 12 | export async function getLlamaCppBatchVector(texts, apiUrl, directories) { |
| 13 | 13 | const url = new URL(apiUrl); |
| 14 | 14 | url.pathname = '/v1/embeddings'; |
| 15 | 15 | |
| @@ -30,6 +30,7 @@ async function getLlamaCppBatchVector(texts, apiUrl, directories) { | ||
| 30 | 30 | throw new Error(`LlamaCpp: Failed to get vector for text: ${response.statusText} ${responseText}`); |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | + /** @type {any} */ | |
| 33 | 34 | const data = await response.json(); |
| 34 | 35 | |
| 35 | 36 | if (!Array.isArray(data?.data)) { |
| @@ -47,15 +48,10 @@ async function getLlamaCppBatchVector(texts, apiUrl, directories) { | ||
| 47 | 48 | * Gets the vector for the given text from LlamaCpp |
| 48 | 49 | * @param {string} text - The text to get the vector for |
| 49 | 50 | * @param {string} apiUrl - The API URL |
| 50 | 51 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 51 | 52 | * @returns {Promise<number[]>} - The vector for the text |
| 52 | 53 | */ |
| 53 | 54 | export async function getLlamaCppVector(text, apiUrl, directories) { |
| 54 | 55 | const vectors = await getLlamaCppBatchVector([text], apiUrl, directories); |
| 55 | 56 | return vectors[0]; |
| 56 | 57 | } |
| 57 | - | |
| 58 | -module.exports = { | |
| 59 | - getLlamaCppBatchVector, | |
| 60 | - getLlamaCppVector, | |
| 61 | -}; | |
| @@ -1,14 +1,14 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport { SECRET_KEYS, readSecret } =from require('../endpoints/secrets.js'); |
| 3 | 3 | const API_MAKERSUITE = 'https://generativelanguage.googleapis.com'; |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * Gets the vector for the given text from gecko model |
| 7 | 7 | * @param {string[]} texts - The array of texts to get the vector for |
| 8 | 8 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 9 | 9 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 10 | 10 | */ |
| 11 | 11 | export async function getMakerSuiteBatchVector(texts, directories) { |
| 12 | 12 | const promises = texts.map(text => getMakerSuiteVector(text, directories)); |
| 13 | 13 | return await Promise.all(promises); |
| 14 | 14 | } |
| @@ -16,10 +16,10 @@ async function getMakerSuiteBatchVector(texts, directories) { | ||
| 16 | 16 | /** |
| 17 | 17 | * Gets the vector for the given text from Gemini API text-embedding-004 model |
| 18 | 18 | * @param {string} text - The text to get the vector for |
| 19 | 19 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 20 | 20 | * @returns {Promise<number[]>} - The vector for the text |
| 21 | 21 | */ |
| 22 | 22 | export async function getMakerSuiteVector(text, directories) { |
| 23 | 23 | const key = readSecret(directories, SECRET_KEYS.MAKERSUITE); |
| 24 | 24 | |
| 25 | 25 | if (!key) { |
| @@ -52,12 +52,8 @@ async function getMakerSuiteVector(text, directories) { | ||
| 52 | 52 | throw new Error('Google AI Studio request failed'); |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | + /** @type {any} */ | |
| 55 | 56 | const data = await response.json(); |
| 56 | 57 | // noinspection JSValidateTypes |
| 57 | 58 | return data['embedding']['values']; |
| 58 | 59 | } |
| 59 | - | |
| 60 | -module.exports = { | |
| 61 | - getMakerSuiteVector, | |
| 62 | - getMakerSuiteBatchVector, | |
| 63 | -}; | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport { SECRET_KEYS, readSecret } =from require('../endpoints/secrets.js'); |
| 3 | 3 | |
| 4 | 4 | const SOURCES = { |
| 5 | 5 | 'nomicai': { |
| @@ -13,10 +13,10 @@ const SOURCES = { | ||
| 13 | 13 | * Gets the vector for the given text batch from an OpenAI compatible endpoint. |
| 14 | 14 | * @param {string[]} texts - The array of texts to get the vector for |
| 15 | 15 | * @param {string} source - The source of the vector |
| 16 | 16 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 17 | 17 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 18 | 18 | */ |
| 19 | 19 | export async function getNomicAIBatchVector(texts, source, directories) { |
| 20 | 20 | const config = SOURCES[source]; |
| 21 | 21 | |
| 22 | 22 | if (!config) { |
| @@ -51,6 +51,7 @@ async function getNomicAIBatchVector(texts, source, directories) { | ||
| 51 | 51 | throw new Error('API request failed'); |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | + /** @type {any} */ | |
| 54 | 55 | const data = await response.json(); |
| 55 | 56 | if (!Array.isArray(data?.embeddings)) { |
| 56 | 57 | console.log('API response was not an array'); |
| @@ -64,15 +65,10 @@ async function getNomicAIBatchVector(texts, source, directories) { | ||
| 64 | 65 | * Gets the vector for the given text from an OpenAI compatible endpoint. |
| 65 | 66 | * @param {string} text - The text to get the vector for |
| 66 | 67 | * @param {string} source - The source of the vector |
| 67 | 68 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 68 | 69 | * @returns {Promise<number[]>} - The vector for the text |
| 69 | 70 | */ |
| 70 | 71 | export async function getNomicAIVector(text, source, directories) { |
| 71 | 72 | const vectors = await getNomicAIBatchVector([text], source, directories); |
| 72 | 73 | return vectors[0]; |
| 73 | 74 | } |
| 74 | - | |
| 75 | -module.exports = { | |
| 76 | - getNomicAIVector, | |
| 77 | - getNomicAIBatchVector, | |
| 78 | -}; | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport { setAdditionalHeadersByType } =from require('../additional-headers.js'); |
| 3 | 3 | constimport { TEXTGEN_TYPES } =from require('../constants.js'); |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * Gets the vector for the given text from Ollama |
| @@ -8,10 +8,10 @@ const { TEXTGEN_TYPES } = require('../constants'); | ||
| 8 | 8 | * @param {string} apiUrl - The API URL |
| 9 | 9 | * @param {string} model - The model to use |
| 10 | 10 | * @param {boolean} keep - Keep the model loaded in memory |
| 11 | 11 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 12 | 12 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 13 | 13 | */ |
| 14 | 14 | export async function getOllamaBatchVector(texts, apiUrl, model, keep, directories) { |
| 15 | 15 | const result = []; |
| 16 | 16 | for (const text of texts) { |
| 17 | 17 | const vector = await getOllamaVector(text, apiUrl, model, keep, directories); |
| @@ -26,10 +26,10 @@ async function getOllamaBatchVector(texts, apiUrl, model, keep, directories) { | ||
| 26 | 26 | * @param {string} apiUrl - The API URL |
| 27 | 27 | * @param {string} model - The model to use |
| 28 | 28 | * @param {boolean} keep - Keep the model loaded in memory |
| 29 | 29 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 30 | 30 | * @returns {Promise<number[]>} - The vector for the text |
| 31 | 31 | */ |
| 32 | 32 | export async function getOllamaVector(text, apiUrl, model, keep, directories) { |
| 33 | 33 | const url = new URL(apiUrl); |
| 34 | 34 | url.pathname = '/api/embeddings'; |
| 35 | 35 | |
| @@ -54,6 +54,7 @@ async function getOllamaVector(text, apiUrl, model, keep, directories) { | ||
| 54 | 54 | throw new Error(`Ollama: Failed to get vector for text: ${response.statusText} ${responseText}`); |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | + /** @type {any} */ | |
| 57 | 58 | const data = await response.json(); |
| 58 | 59 | |
| 59 | 60 | if (!Array.isArray(data?.embedding)) { |
| @@ -62,8 +63,3 @@ async function getOllamaVector(text, apiUrl, model, keep, directories) { | ||
| 62 | 63 | |
| 63 | 64 | return data.embedding; |
| 64 | 65 | } |
| 65 | - | |
| 66 | -module.exports = { | |
| 67 | - getOllamaBatchVector, | |
| 68 | - getOllamaVector, | |
| 69 | -}; | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport { SECRET_KEYS, readSecret } =from require('../endpoints/secrets.js'); |
| 3 | 3 | |
| 4 | 4 | const SOURCES = { |
| 5 | 5 | 'togetherai': { |
| @@ -23,11 +23,11 @@ const SOURCES = { | ||
| 23 | 23 | * Gets the vector for the given text batch from an OpenAI compatible endpoint. |
| 24 | 24 | * @param {string[]} texts - The array of texts to get the vector for |
| 25 | 25 | * @param {string} source - The source of the vector |
| 26 | 26 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 27 | 27 | * @param {string} model - The model to use for the embedding |
| 28 | 28 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 29 | 29 | */ |
| 30 | 30 | export async function getOpenAIBatchVector(texts, source, directories, model = '') { |
| 31 | 31 | const config = SOURCES[source]; |
| 32 | 32 | |
| 33 | 33 | if (!config) { |
| @@ -61,6 +61,7 @@ async function getOpenAIBatchVector(texts, source, directories, model = '') { | ||
| 61 | 61 | throw new Error('API request failed'); |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | + /** @type {any} */ | |
| 64 | 65 | const data = await response.json(); |
| 65 | 66 | |
| 66 | 67 | if (!Array.isArray(data?.data)) { |
| @@ -79,16 +80,11 @@ async function getOpenAIBatchVector(texts, source, directories, model = '') { | ||
| 79 | 80 | * Gets the vector for the given text from an OpenAI compatible endpoint. |
| 80 | 81 | * @param {string} text - The text to get the vector for |
| 81 | 82 | * @param {string} source - The source of the vector |
| 82 | 83 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 83 | 84 | * @param {string} model - The model to use for the embedding |
| 84 | 85 | * @returns {Promise<number[]>} - The vector for the text |
| 85 | 86 | */ |
| 86 | 87 | export async function getOpenAIVector(text, source, directories, model = '') { |
| 87 | 88 | const vectors = await getOpenAIBatchVector([text], source, directories, model); |
| 88 | 89 | return vectors[0]; |
| 89 | 90 | } |
| 90 | - | |
| 91 | -module.exports = { | |
| 92 | - getOpenAIVector, | |
| 93 | - getOpenAIBatchVector, | |
| 94 | -}; | |
| @@ -1,16 +1,16 @@ | ||
| 1 | 1 | constimport fetch =from require('node-fetch').default; |
| 2 | 2 | constimport { setAdditionalHeadersByType } =from require('../additional-headers.js'); |
| 3 | 3 | constimport { TEXTGEN_TYPES } =from require('../constants.js'); |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * Gets the vector for the given text from VLLM |
| 7 | 7 | * @param {string[]} texts - The array of texts to get the vectors for |
| 8 | 8 | * @param {string} apiUrl - The API URL |
| 9 | 9 | * @param {string} model - The model to use |
| 10 | 10 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 11 | 11 | * @returns {Promise<number[][]>} - The array of vectors for the texts |
| 12 | 12 | */ |
| 13 | 13 | export async function getVllmBatchVector(texts, apiUrl, model, directories) { |
| 14 | 14 | const url = new URL(apiUrl); |
| 15 | 15 | url.pathname = '/v1/embeddings'; |
| 16 | 16 | |
| @@ -31,6 +31,7 @@ async function getVllmBatchVector(texts, apiUrl, model, directories) { | ||
| 31 | 31 | throw new Error(`VLLM: Failed to get vector for text: ${response.statusText} ${responseText}`); |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | + /** @type {any} */ | |
| 34 | 35 | const data = await response.json(); |
| 35 | 36 | |
| 36 | 37 | if (!Array.isArray(data?.data)) { |
| @@ -49,15 +50,10 @@ async function getVllmBatchVector(texts, apiUrl, model, directories) { | ||
| 49 | 50 | * @param {string} text - The text to get the vector for |
| 50 | 51 | * @param {string} apiUrl - The API URL |
| 51 | 52 | * @param {string} model - The model to use |
| 52 | 53 | * @param {import('../users.js').UserDirectoryList} directories - The directories object for the user |
| 53 | 54 | * @returns {Promise<number[]>} - The vector for the text |
| 54 | 55 | */ |
| 55 | 56 | export async function getVllmVector(text, apiUrl, model, directories) { |
| 56 | 57 | const vectors = await getVllmBatchVector([text], apiUrl, model, directories); |
| 57 | 58 | return vectors[0]; |
| 58 | 59 | } |
| 59 | - | |
| 60 | -module.exports = { | |
| 61 | - getVllmBatchVector, | |
| 62 | - getVllmVector, | |
| 63 | -}; | |