diff --git a/.gitignore b/.gitignore index a3e39cb09..ab258371e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ content.log cloudflared.exe public/assets/ access.log +/vectors/ +/cache/ diff --git a/default/config.conf b/default/config.conf index 962c6ce5b..5e9d2920f 100644 --- a/default/config.conf +++ b/default/config.conf @@ -15,7 +15,15 @@ const skipContentCheck = false; // If true, no new default content will be deliv // Change this setting only on "trusted networks". Do not change this value unless you are aware of the issues that can arise from changing this setting and configuring a insecure setting. const securityOverride = false; +// Additional settings for extra modules / extensions +const extras = { + // Text classification model for sentiment analysis. HuggingFace ID of a model in ONNX format. + classificationModel: 'Cohee/distilbert-base-uncased-go-emotions-onnx', +}; + // Request overrides for additional headers +// Format is an array of objects: +// { hosts: [ "" ], headers: {
: "" } } const requestOverrides = []; module.exports = { @@ -32,4 +40,5 @@ module.exports = { securityOverride, skipContentCheck, requestOverrides, + extras, }; diff --git a/package-lock.json b/package-lock.json index 9bde269ce..a450a34cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,19 @@ { "name": "sillytavern", - "version": "1.10.2", + "version": "1.10.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sillytavern", - "version": "1.10.2", + "version": "1.10.3", "license": "AGPL-3.0", "dependencies": { "@agnai/sentencepiece-js": "^1.1.1", "@agnai/web-tokenizers": "^0.1.3", "@dqbd/tiktoken": "^1.0.2", + "@tensorflow-models/universal-sentence-encoder": "^1.3.3", + "@tensorflow/tfjs": "^4.10.0", "command-exists": "^1.2.9", "compression": "^1", "cookie-parser": "^1.4.6", @@ -24,7 +26,7 @@ "gpt3-tokenizer": "^1.1.5", "ip-matching": "^2.1.2", "ipaddr.js": "^2.0.1", - "jimp": "^0.22.7", + "jimp": "^0.22.10", "jquery": "^3.6.4", "json5": "^2.2.3", "lodash": "^4.17.21", @@ -38,8 +40,10 @@ "png-chunks-extract": "^1.0.0", "response-time": "^2.3.2", "sanitize-filename": "^1.6.3", + "sillytavern-transformers": "^2.7.3", "simple-git": "^3.19.1", "uniqolor": "^1.1.0", + "vectra": "^0.2.2", "webp-converter": "2.3.2", "write-file-atomic": "^5.0.1", "ws": "^8.13.0", @@ -131,11 +135,11 @@ "integrity": "sha512-bhR5k5W+8GLzysjk8zTMVygQZsgvf7W1F0IlL4ZQ5ugjo5rCyiwGM5d8DYriXspytfu98tv59niang3/T+FoDw==" }, "node_modules/@jimp/bmp": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.8.tgz", - "integrity": "sha512-JEMKgM1AEvvWfn9ZCHn62nK+QCE3Pb/ZhPdL3NF0ZgKNww6pqOmo6KqXzqY18JLB7c0epuTp4GPDPDhOh/ou1g==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.10.tgz", + "integrity": "sha512-1UXRl1Nw1KptZ1r0ANqtXOst9vGH51dq7keVKQzyyTO2lz4dOaezS9StuSTNh+RmiHg/SVPaFRpPfB0S/ln4Kg==", "dependencies": { - "@jimp/utils": "^0.22.8", + "@jimp/utils": "^0.22.10", "bmp-js": "^0.1.0" }, "peerDependencies": { @@ -143,36 +147,35 @@ } }, "node_modules/@jimp/core": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.8.tgz", - "integrity": "sha512-vkN28aFikzQieA6bGxN+qe20pseCAemCyUI0YmRkJIArlb6OujtAwWAKyokv2lylV56bq8EQGIz+Y30OXUnRqg==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.10.tgz", + "integrity": "sha512-ZKyrehVy6wu1PnBXIUpn/fXmyMRQiVSbvHDubgXz4bfTOao3GiOurKHjByutQIgozuAN6ZHWiSge1dKA+dex3w==", "dependencies": { - "@jimp/utils": "^0.22.8", + "@jimp/utils": "^0.22.10", "any-base": "^1.1.0", "buffer": "^5.2.0", "exif-parser": "^0.1.12", "file-type": "^16.5.4", "isomorphic-fetch": "^3.0.0", - "mkdirp": "^2.1.3", "pixelmatch": "^4.0.2", "tinycolor2": "^1.6.0" } }, "node_modules/@jimp/custom": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.8.tgz", - "integrity": "sha512-u6lP9x/HNeGHB0Oojv4c2mhuDvn7G0ikzYbK4IKLsH4HzHxt62faMjBzQMcFhKJhR6UiiKE/jiHrhGvBT/fMkw==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.10.tgz", + "integrity": "sha512-sPZkUYe1hu0iIgNisjizxPJqq2vaaKvkCkPoXq2U6UV3ZA1si/WVdrg25da3IcGIEV+83AoHgM8TvqlLgrCJsg==", "dependencies": { - "@jimp/core": "^0.22.8" + "@jimp/core": "^0.22.10" } }, "node_modules/@jimp/gif": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.8.tgz", - "integrity": "sha512-I0l6koS67IPU40RPxCJTD1NvePEd8vUIHTejx1ly0jrjGnumbqdarAlBUkDrKfPPc+Fnqp84hBbSN1w5hNPT6w==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.10.tgz", + "integrity": "sha512-yEX2dSpamvkSx1PPDWGnKeWDrBz0vrCKjVG/cn4Zr68MRRT75tbZIeOrBa+RiUpY3ho5ix7d36LkYvt3qfUIhQ==", "dependencies": { - "@jimp/utils": "^0.22.8", - "gifwrap": "^0.9.2", + "@jimp/utils": "^0.22.10", + "gifwrap": "^0.10.1", "omggif": "^1.0.9" }, "peerDependencies": { @@ -180,11 +183,11 @@ } }, "node_modules/@jimp/jpeg": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.8.tgz", - "integrity": "sha512-hLXrQ7/0QiUhAVAF10dfGCSq3hvyqjKltlpu/87b3wqMDKe9KdvhX1AJHiUUrAbJv1fAcnOmQGTyXGuySa1D6A==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.10.tgz", + "integrity": "sha512-6bu98pAcVN4DY2oiDLC4TOgieX/lZrLd1tombWZOFCN5PBmqaHQxm7IUmT+Wj4faEvh8QSHgVLSA+2JQQRJWVA==", "dependencies": { - "@jimp/utils": "^0.22.8", + "@jimp/utils": "^0.22.10", "jpeg-js": "^0.4.4" }, "peerDependencies": { @@ -192,44 +195,44 @@ } }, "node_modules/@jimp/plugin-blit": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.8.tgz", - "integrity": "sha512-rQ19txVCKIwo74HtgFodFt4//0ATPCJK+f24riqzb+nx+1JaOo1xRvpJqg4moirHwKR2fhwdDxmY7KX20kCeYA==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.10.tgz", + "integrity": "sha512-6EI8Sl+mxYHEIy6Yteh6eknD+EZguKpNdr3sCKxNezmLR0+vK99vHcllo6uGSjXXiwtwS67Xqxn8SsoatL+UJQ==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-blur": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.8.tgz", - "integrity": "sha512-GWbNK3YW6k2EKiGJdpAFEr0jezPBtiVxj2wG/lCPuWJz7KmzSSN99hQjIy73xQxoBCRdALfJlkhe3leFNRueSQ==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.10.tgz", + "integrity": "sha512-4XRTWuPVdMXJeclJMisXPGizeHtTryVaVV5HnuQXpKqIZtzXReCCpNGH8q/i0kBQOQMXhGWS3mpqOEwtpPePKw==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-circle": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.8.tgz", - "integrity": "sha512-qPCw8XFW8opT89ciFDuvs+eB3EB1mZIJWVajD2qAlprHiE7YGr34TkM7N5MNr3qZ1pJgkYdW6+HbBrJwBaonqw==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.10.tgz", + "integrity": "sha512-mhcwTO1ywRxiCgtLGge6tDDIDPlX6qkI3CY+BjgGG/XhVHccCddXgOGLdlf+5OuKIEF2Nqs0V01LQEQIJFTmEw==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-color": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.8.tgz", - "integrity": "sha512-ogkbg6rpDVH/mMLgAQKg17z3oZE0VN7ZWxNoH12fUHchqKz1I57zpa65fxZe2I8T5Xz97HR3x+7V7oI8qQGdSA==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.10.tgz", + "integrity": "sha512-e4t3L7Kedd96E0x1XjsTM6NcgulKUU66HdFTao7Tc9FYJRFSlttARZ/C6LEryGDm/i69R6bJEpo7BkNz0YL55Q==", "dependencies": { - "@jimp/utils": "^0.22.8", + "@jimp/utils": "^0.22.10", "tinycolor2": "^1.6.0" }, "peerDependencies": { @@ -237,11 +240,11 @@ } }, "node_modules/@jimp/plugin-contain": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.8.tgz", - "integrity": "sha512-oiaPLdJt9Dk+XEEhM/OU3lFemM51mA9NgMCAdburSCjDzKacJYBGFSHjTOhXzcxOie/ZDpOYN/UzFGKy8Dgl9A==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.10.tgz", + "integrity": "sha512-eP8KrzctuEoqibQAxi9WhbnoRosydhiwg+IYya3dKuKDBTrD9UHt+ERlPQ/lTNWHzV/l4S1ntV3r9s9saJgsXA==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -251,11 +254,11 @@ } }, "node_modules/@jimp/plugin-cover": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.8.tgz", - "integrity": "sha512-mO68w1m/LhfuHU8LKHY05a4/hhWnY4t+T+8JCw9t+5yfzA4+LofBZZKtFtWgwf/QGe1y3X2rtUU/avAzDUKyyA==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.10.tgz", + "integrity": "sha512-kJCwL5T1igfa0InCfkE7bBeqg26m46aoRt10ug+rvm11P6RrvRMGrgINFyIKB+mnB7CiyBN/MOula1CvLhSInQ==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -265,55 +268,55 @@ } }, "node_modules/@jimp/plugin-crop": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.8.tgz", - "integrity": "sha512-ns4oH0h0gezYsbuH8RThcMLY5uTLk/vnqOVjWCehMHEzxi0DHMWCmpcb6bC//vJ+XFNhtVGn1ALN7+ROmPrj+A==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.10.tgz", + "integrity": "sha512-BOZ+YGaZlhU7c5ye65RxikicXH0Ki0It6/XHISvipR5WZrfjLjL2Ke20G+AGnwBQc76gKenVcMXVUCnEjtZV+Q==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-displace": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.8.tgz", - "integrity": "sha512-Cj8nHYgsdFynOIx3dbbiVwRuZn3xO+RVfwkTRy0JBye+K2AU8SQJS+hSFNMQFTZt5djivh6kh0TzvR/6LkOd1w==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.10.tgz", + "integrity": "sha512-llNiWWMTKISDXt5+cXI0GaFmZWAjlT+4fFLYf4eXquuL/9wZoQsEBhv2GdGd48mkiS8jZq1Nnb2Q4ehEPTvrzw==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-dither": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.8.tgz", - "integrity": "sha512-oE0Us/6bEgrgEg56plU3jSBzvB9iGhweKUHmxYMWnQbFCHP4mNCtPAs8+Fmq6c+m98ZgBgRcrJTnC7lphHkGyw==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.10.tgz", + "integrity": "sha512-05WLmeV5M+P/0FS+bWf13hMew2X0oa8w9AtmevL2UyA/5GqiyvP2Xm5WfGQ8oFiiMvpnL6RFomJQOZtWca0C2w==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-fisheye": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.8.tgz", - "integrity": "sha512-bWvYY/nfMcKclWEaRyAir+YsT6C5St823HUQAsewZowTrJmme+w4U2a6InsryTHUL01BBcV5BLH0aDHuV3StvA==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.10.tgz", + "integrity": "sha512-InjiXvc7Gkzrx8VWtU97kDqV7ENnhHGPULymJWeZaF2aicud9Fpk4iCtd/DcZIrk7Cbe60A8RwNXN00HXIbSCg==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-flip": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.8.tgz", - "integrity": "sha512-0NFTNzjsdmOQkaIkNjZqO3/yU4SQb9nnWQXsLS1fFo+9QrIL5v8vVkXpk/rhiND6PyTj2mMTNjOa76GuZcC+iQ==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.10.tgz", + "integrity": "sha512-42GkGtTHWnhnwTMPVK/kXObZbkYIpQWfuIfy5EMEMk6zRj05zpv4vsjkKWfuemweZINwfvD7wDJF7FVFNNcZZg==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -321,55 +324,55 @@ } }, "node_modules/@jimp/plugin-gaussian": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.8.tgz", - "integrity": "sha512-E/f14aLzCS50QAM7K+InI9V61KVy/Zx52vy7Jjfo1h7qKhQHss3PYaydaH0N6qlXRNeXgh+4/32P9JfieLMcdw==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.10.tgz", + "integrity": "sha512-ykrG/6lTp9Q5YA8jS5XzwMHtRxb9HOFMgtmnrUZ8kU+BK8REecfy9Ic5BUEOjCYvS1a/xLsnrZQU07iiYxBxFg==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-invert": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.8.tgz", - "integrity": "sha512-UauP39FF2cwbA5VU+Tz9VlNa9rtULPSHZb0Huwcjqjm9/G/xVN69VJ8+RKiFC4zM1/kYAUp/6IRwPa6qdKJpSw==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.10.tgz", + "integrity": "sha512-d8j9BlUJYs/c994t4azUWSWmQq4LLPG4ecm8m6SSNqap+S/HlVQGqjYhJEBbY9EXkOTYB9vBL9bqwSM1Rr6paA==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-mask": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.8.tgz", - "integrity": "sha512-bhg5+3i8x1CmYj6cjvPBQZLwZEI3iK3gJWF25ZHF+12d3cqDuJngtr8oRQOQLlAgvKmrj9FXIiEPDczUI9cnWQ==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.10.tgz", + "integrity": "sha512-yRBs1230XZkz24uFTdTcSlZ0HXZpIWzM3iFQN56MzZ7USgdVZjPPDCQ8I9RpqfZ36nDflQkUO0wV7ucsi4ogow==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-normalize": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.8.tgz", - "integrity": "sha512-Yg5nreAR1JYuSObu3ExlgaLxVeW6VvjVL5qFwiPFxSNlG8JIwL1Ir3K3ChSnnvymyZvJMHb6YKTYNfXKw5Da6g==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.10.tgz", + "integrity": "sha512-Wk9GX6eJMchX/ZAazVa70Fagu+OXMvHiPY+HrcEwcclL+p1wo8xAHEsf9iKno7Ja4EU9lLhbBRY5hYJyiKMEkg==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-print": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.8.tgz", - "integrity": "sha512-86O5ejCDi543IYl0TykSmNWErzAjEYhiAxNQb2F7rFRT38WJYNVsvJ6QhxhDQHKxSmF5iwmqbk0jYk5Wp2Z1kw==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.10.tgz", + "integrity": "sha512-1U3VloIR+beE1kWPdGEJMiE2h1Do29iv3w8sBbvPyRP4qXxRFcDpmCGtctsrKmb1krlBFlj8ubyAY90xL+5n9w==", "dependencies": { - "@jimp/utils": "^0.22.8", + "@jimp/utils": "^0.22.10", "load-bmfont": "^1.4.1" }, "peerDependencies": { @@ -378,22 +381,22 @@ } }, "node_modules/@jimp/plugin-resize": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.8.tgz", - "integrity": "sha512-kg8ArQRPqv/iU3DWNXCa8kcVIhoq64Ze0aGCAeFLKlAq/59f5pzAci6m6vV4L/uOVdYmUa9/kYwIFY6RWKpfzQ==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.10.tgz", + "integrity": "sha512-ixomxVcnAONXDgaq0opvAx4UAOiEhOA/tipuhFFOvPKFd4yf1BAnEviB5maB0SBHHkJXPUSzDp/73xVTMGSe7g==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-rotate": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.8.tgz", - "integrity": "sha512-9a+VPZWMN/Cks76wf8LjM5RVA3ntP9+NAdsS1SZhhXel7U3Re/dWMouIEbo3QTt6K+igRo4txUCdZiw4ZucvkQ==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.10.tgz", + "integrity": "sha512-eeFX8dnRyf3LAdsdXWKWuN18hLRg8zy1cP0cP9rHzQVWRK7ck/QsLxK1vHq7MADGwQalNaNTJ9SQxH6c8mz6jw==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -403,11 +406,11 @@ } }, "node_modules/@jimp/plugin-scale": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.8.tgz", - "integrity": "sha512-dQS4pG6DX6endu8zUpvBBOEtGC+ljDDDNw0scSXY71TxyQdNo5Ro0apfsppjmuAr8rNotRkfyxbITKkXQDRUDQ==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.10.tgz", + "integrity": "sha512-TG/H0oUN69C9ArBCZg4PmuoixFVKIiru8282KzSB/Tp1I0xwX0XLTv3dJ5pobPlIgPcB+TmD4xAIdkCT4rtWxg==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -415,11 +418,11 @@ } }, "node_modules/@jimp/plugin-shadow": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.8.tgz", - "integrity": "sha512-HyAhr7OblTQh+BoKHQg4qbS9MweNlH77yfpBqUEyDtfyjI5r06+5chf1ZdLRIPEWv/BdCfdI/g81Wv69muCMwA==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.10.tgz", + "integrity": "sha512-TN9xm6fI7XfxbMUQqFPZjv59Xdpf0tSiAQdINB4g6pJMWiVANR/74OtDONoy3KKpenu5Y38s+FkrtID/KcQAhw==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -428,11 +431,11 @@ } }, "node_modules/@jimp/plugin-threshold": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.8.tgz", - "integrity": "sha512-ZmkfH0PtjvF1UcKsjw0H7V6r+LC0yKzEfg76Jhs2nIqIgsxsSOVfHwS7z0/1IWnyXxSw36m+NjCAotNHRILGmA==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.10.tgz", + "integrity": "sha512-DA2lSnU0TgIRbAgmXaxroYw3Ad6J2DOFEoJp0NleSm2h3GWbZEE5yW9U2B6hD3iqn4AenG4E2b2WzHXZyzSutw==", "dependencies": { - "@jimp/utils": "^0.22.8" + "@jimp/utils": "^0.22.10" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -441,31 +444,31 @@ } }, "node_modules/@jimp/plugins": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.8.tgz", - "integrity": "sha512-ieI2+kCpmIfjwVlT7B67ULCzxMizfj7LspJh9HnIZCDXQB9GBOZ9KImLYc75Krae0dP/3FR7FglLiSI7fkOHbw==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.10.tgz", + "integrity": "sha512-KDMZyM6pmvS8freB+UBLko1TO/k4D7URS/nphCozuH+P7i3UMe7NdckXKJ8u+WD6sqN0YFYvBehpkpnUiw/91w==", "dependencies": { - "@jimp/plugin-blit": "^0.22.8", - "@jimp/plugin-blur": "^0.22.8", - "@jimp/plugin-circle": "^0.22.8", - "@jimp/plugin-color": "^0.22.8", - "@jimp/plugin-contain": "^0.22.8", - "@jimp/plugin-cover": "^0.22.8", - "@jimp/plugin-crop": "^0.22.8", - "@jimp/plugin-displace": "^0.22.8", - "@jimp/plugin-dither": "^0.22.8", - "@jimp/plugin-fisheye": "^0.22.8", - "@jimp/plugin-flip": "^0.22.8", - "@jimp/plugin-gaussian": "^0.22.8", - "@jimp/plugin-invert": "^0.22.8", - "@jimp/plugin-mask": "^0.22.8", - "@jimp/plugin-normalize": "^0.22.8", - "@jimp/plugin-print": "^0.22.8", - "@jimp/plugin-resize": "^0.22.8", - "@jimp/plugin-rotate": "^0.22.8", - "@jimp/plugin-scale": "^0.22.8", - "@jimp/plugin-shadow": "^0.22.8", - "@jimp/plugin-threshold": "^0.22.8", + "@jimp/plugin-blit": "^0.22.10", + "@jimp/plugin-blur": "^0.22.10", + "@jimp/plugin-circle": "^0.22.10", + "@jimp/plugin-color": "^0.22.10", + "@jimp/plugin-contain": "^0.22.10", + "@jimp/plugin-cover": "^0.22.10", + "@jimp/plugin-crop": "^0.22.10", + "@jimp/plugin-displace": "^0.22.10", + "@jimp/plugin-dither": "^0.22.10", + "@jimp/plugin-fisheye": "^0.22.10", + "@jimp/plugin-flip": "^0.22.10", + "@jimp/plugin-gaussian": "^0.22.10", + "@jimp/plugin-invert": "^0.22.10", + "@jimp/plugin-mask": "^0.22.10", + "@jimp/plugin-normalize": "^0.22.10", + "@jimp/plugin-print": "^0.22.10", + "@jimp/plugin-resize": "^0.22.10", + "@jimp/plugin-rotate": "^0.22.10", + "@jimp/plugin-scale": "^0.22.10", + "@jimp/plugin-shadow": "^0.22.10", + "@jimp/plugin-threshold": "^0.22.10", "timm": "^1.6.1" }, "peerDependencies": { @@ -473,11 +476,11 @@ } }, "node_modules/@jimp/png": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.8.tgz", - "integrity": "sha512-XOj11kcCr8zKg24QSwlRfH9k4hbV6rkMGUVxMS3puRzzB0FBSQy42NBYEfYf2XlY2QJSAByPl4AYerOtKb805w==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.10.tgz", + "integrity": "sha512-RYinU7tZToeeR2g2qAMn42AU+8OUHjXPKZZ9RkmoL4bguA1xyZWaSdr22/FBkmnHhOERRlr02KPDN1OTOYHLDQ==", "dependencies": { - "@jimp/utils": "^0.22.8", + "@jimp/utils": "^0.22.10", "pngjs": "^6.0.0" }, "peerDependencies": { @@ -485,9 +488,9 @@ } }, "node_modules/@jimp/tiff": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.8.tgz", - "integrity": "sha512-K0hYUVW5MLgwq3jiHVHa6LvP05J1rXOlRCC+5dMTUnAXVwi45+MKsqA/8lzzwhHYJ65CNhZwy6D3+ZNzM9SIBQ==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.10.tgz", + "integrity": "sha512-OaivlSYzpNTHyH/h7pEtl3A7F7TbsgytZs52GLX/xITW92ffgDgT6PkldIrMrET6ERh/hdijNQiew7IoEEr2og==", "dependencies": { "utif2": "^4.0.1" }, @@ -496,15 +499,15 @@ } }, "node_modules/@jimp/types": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.8.tgz", - "integrity": "sha512-9+xc+mzuYwu0i+6dsnhXiUgfcS+Ktqn5q2jczoKyyBT0cOKgsk+57EIeFLgpTfVGRKRR0y/UIdHByeCzGguF3A==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.10.tgz", + "integrity": "sha512-u/r+XYzbCx4zZukDmxx8S0er3Yq3iDPI6+31WKX0N18i2qPPJYcn8qwIFurfupRumGvJ8SlGLCgt/T+Y8zzUIw==", "dependencies": { - "@jimp/bmp": "^0.22.8", - "@jimp/gif": "^0.22.8", - "@jimp/jpeg": "^0.22.8", - "@jimp/png": "^0.22.8", - "@jimp/tiff": "^0.22.8", + "@jimp/bmp": "^0.22.10", + "@jimp/gif": "^0.22.10", + "@jimp/jpeg": "^0.22.10", + "@jimp/png": "^0.22.10", + "@jimp/tiff": "^0.22.10", "timm": "^1.6.1" }, "peerDependencies": { @@ -512,9 +515,9 @@ } }, "node_modules/@jimp/utils": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.8.tgz", - "integrity": "sha512-AaqjfqDeLzSFzrbGRKHMXg/ntiWKvoG9tpVgWzgOx5/gPWj/IyGfztojLTTvY8HqZCr25z8z91u2lAQD2v46Jw==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.10.tgz", + "integrity": "sha512-ztlOK9Mm2iLG2AMoabzM4i3WZ/FtshcgsJCbZCRUs/DKoeS2tySRJTnQZ1b7Roq0M4Ce+FUAxnCAcBV0q7PH9w==", "dependencies": { "regenerator-runtime": "^0.13.3" } @@ -642,16 +645,328 @@ "node": ">= 8" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tensorflow-models/universal-sentence-encoder": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@tensorflow-models/universal-sentence-encoder/-/universal-sentence-encoder-1.3.3.tgz", + "integrity": "sha512-mipL7ad0CW6uQ68FUkNgkNj/zgA4qgBnNcnMMkNTdL9MUMnzCxu3AE8pWnx2ReKHwdqEG4e8IpaYKfH4B8bojg==", + "peerDependencies": { + "@tensorflow/tfjs-converter": "^3.6.0", + "@tensorflow/tfjs-core": "^3.6.0" + } + }, + "node_modules/@tensorflow/tfjs": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-4.10.0.tgz", + "integrity": "sha512-16q6CcGEoxfg3kimBzNBrZg069GRd4Du49uyQHUMsEvpHbmU0ZpWa2zhjjZ71GlURpbD1LZIZNp6cd2Q1Eqjow==", + "dependencies": { + "@tensorflow/tfjs-backend-cpu": "4.10.0", + "@tensorflow/tfjs-backend-webgl": "4.10.0", + "@tensorflow/tfjs-converter": "4.10.0", + "@tensorflow/tfjs-core": "4.10.0", + "@tensorflow/tfjs-data": "4.10.0", + "@tensorflow/tfjs-layers": "4.10.0", + "argparse": "^1.0.10", + "chalk": "^4.1.0", + "core-js": "3.29.1", + "regenerator-runtime": "^0.13.5", + "yargs": "^16.0.3" + }, + "bin": { + "tfjs-custom-module": "dist/tools/custom_module/cli.js" + } + }, + "node_modules/@tensorflow/tfjs-converter": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-3.21.0.tgz", + "integrity": "sha512-12Y4zVDq3yW+wSjSDpSv4HnpL2sDZrNiGSg8XNiDE4HQBdjdA+a+Q3sZF/8NV9y2yoBhL5L7V4mMLDdbZBd9/Q==", + "peer": true, + "peerDependencies": { + "@tensorflow/tfjs-core": "3.21.0" + } + }, + "node_modules/@tensorflow/tfjs-core": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-3.21.0.tgz", + "integrity": "sha512-YSfsswOqWfd+M4bXIhT3hwtAb+IV8+ODwIxwdFR/7jTAPZP1wMVnSlpKnXHAN64HFOiP+Tm3HmKusEZ0+09A0w==", + "peer": true, + "dependencies": { + "@types/long": "^4.0.1", + "@types/offscreencanvas": "~2019.3.0", + "@types/seedrandom": "^2.4.28", + "@types/webgl-ext": "0.0.30", + "@webgpu/types": "0.1.16", + "long": "4.0.0", + "node-fetch": "~2.6.1", + "seedrandom": "^3.0.5" + }, + "engines": { + "yarn": ">= 1.3.2" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-backend-cpu": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.10.0.tgz", + "integrity": "sha512-w3f0ORR1smSpkW7om2yQVunRYMjyqWcEbWCPirR1DQ6ImgW+VWqmM2oVPQXRsFYpwg1g6bk2Jp5COafpPA+krw==", + "dependencies": { + "@types/seedrandom": "^2.4.28", + "seedrandom": "^3.0.5" + }, + "engines": { + "yarn": ">= 1.3.2" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "4.10.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-backend-webgl": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.10.0.tgz", + "integrity": "sha512-Vzl/pyXHa9TgFaRJGspExjZVDKgkKvLxOkPaH+psE2LPnQkiH/IOPO7HKO0U3+hZql977BIiZdHc6HNprFS3/A==", + "dependencies": { + "@tensorflow/tfjs-backend-cpu": "4.10.0", + "@types/offscreencanvas": "~2019.3.0", + "@types/seedrandom": "^2.4.28", + "seedrandom": "^3.0.5" + }, + "engines": { + "yarn": ">= 1.3.2" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "4.10.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-converter": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-4.10.0.tgz", + "integrity": "sha512-ffLpK+ismdmiDcoTID2aidP3/uJYyQPjmKdRZ3hBUkrczy7pQIcCW8blIR9Gk20htB4OLQMf74ZxbpfdQ9nYeQ==", + "peerDependencies": { + "@tensorflow/tfjs-core": "4.10.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-core": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.10.0.tgz", + "integrity": "sha512-klc6lUTYRbHQLEFnVKtTICNK+WUlduUcrlXsDs6ixKAOJzLAlIR2JnmJICt2AT2Rxwu0Zj2bAYojSxgcIcUUxA==", + "dependencies": { + "@types/long": "^4.0.1", + "@types/offscreencanvas": "~2019.7.0", + "@types/seedrandom": "^2.4.28", + "@webgpu/types": "0.1.30", + "long": "4.0.0", + "node-fetch": "~2.6.1", + "seedrandom": "^3.0.5" + }, + "engines": { + "yarn": ">= 1.3.2" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-core/node_modules/@types/offscreencanvas": { + "version": "2019.7.1", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.1.tgz", + "integrity": "sha512-+HSrJgjBW77ALieQdMJvXhRZUIRN1597L+BKvsyeiIlHHERnqjcuOLyodK3auJ3Y3zRezNKtKAhuQWYJfEgFHQ==" + }, + "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-data": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-4.10.0.tgz", + "integrity": "sha512-71rQ6xSipXdClKja705jrWZkH9ostAYuVZlf7nW2AJXUCzhrGsJAkcHag4m568mDFoAqfQQTBy4Gk26h0/Y+Pg==", + "dependencies": { + "@types/node-fetch": "^2.1.2", + "node-fetch": "~2.6.1", + "string_decoder": "^1.3.0" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "4.10.0", + "seedrandom": "^3.0.5" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-layers": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-4.10.0.tgz", + "integrity": "sha512-SLZWnuDF98WmmJQ5NhsXJFlJPwoKxfLowvAHTlLz+Q1Po4juZVZ+BkatRsqrI2sA2B0IIu2TJp4VEAFWMqzTTg==", + "peerDependencies": { + "@tensorflow/tfjs-core": "4.10.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/@webgpu/types": { + "version": "0.1.30", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.30.tgz", + "integrity": "sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==" + }, + "node_modules/@tensorflow/tfjs/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/@tensorflow/tfjs/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "node_modules/@types/node": { "version": "16.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" }, + "node_modules/@types/node-fetch": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/offscreencanvas": { + "version": "2019.3.0", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz", + "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==" + }, + "node_modules/@types/seedrandom": { + "version": "2.4.30", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.30.tgz", + "integrity": "sha512-AnxLHewubLVzoF/A4qdxBGHCKifw8cY32iro3DQX9TPcetE95zBeVt3jnsvtvAUf1vwzMfwzp4t/L2yqPlnjkQ==" + }, + "node_modules/@types/webgl-ext": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz", + "integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==", + "peer": true + }, + "node_modules/@webgpu/types": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.16.tgz", + "integrity": "sha512-9E61voMP4+Rze02jlTXud++Htpjyyk8vw5Hyw9FGRrmhHQg2GqbuOfwf5Klrb8vTxc2XWI3EfO7RUHMpxTj26A==", + "peer": true + }, "node_modules/@xmldom/xmldom": { "version": "0.8.9", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.9.tgz", @@ -748,6 +1063,14 @@ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -767,6 +1090,11 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -776,6 +1104,16 @@ "node": ">= 4.0.0" } }, + "node_modules/axios": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -856,6 +1194,11 @@ "node": ">= 0.8" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -947,7 +1290,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -959,6 +1301,42 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -994,6 +1372,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", @@ -1104,6 +1493,16 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-js": { + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", + "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -1137,6 +1536,32 @@ "http-errors": "^2.0.0" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1177,6 +1602,14 @@ "node": ">=8" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1195,9 +1628,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "dev": true, "engines": { "node": ">=8" @@ -1223,11 +1656,70 @@ "node": ">=8" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, "node_modules/dom-walk": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "engines": { + "node": ">=10" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1255,6 +1747,17 @@ "once": "^1.4.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1268,6 +1771,14 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1445,6 +1956,43 @@ "node": ">= 0.8" } }, + "node_modules/flatbuffers": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz", + "integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1520,9 +2068,9 @@ } }, "node_modules/gifwrap": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.9.4.tgz", - "integrity": "sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", + "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" @@ -1580,6 +2128,11 @@ "resolved": "https://registry.npmjs.org/google-translate-api-browser/-/google-translate-api-browser-3.0.1.tgz", "integrity": "sha512-KTLodkyGBWMK9IW6QIeJ2zCuju4Z0CLpbkADKo+yLhbSTD4l+CXXpQ/xaynGVAzeBezzJG6qn8MLeqOq3SmW0A==" }, + "node_modules/gpt-3-encoder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/gpt-3-encoder/-/gpt-3-encoder-1.1.4.tgz", + "integrity": "sha512-fSQRePV+HUAhCn7+7HL7lNIXNm6eaFWFbNLOOGtmSJ0qJycyQvj60OvRlH7mee8xAMjBDNRdMXlMwjAbMTDjkg==" + }, "node_modules/gpt3-tokenizer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/gpt3-tokenizer/-/gpt3-tokenizer-1.1.5.tgz", @@ -1597,6 +2150,11 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==" + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1612,7 +2170,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -1639,6 +2196,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -1880,13 +2455,13 @@ } }, "node_modules/jimp": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.8.tgz", - "integrity": "sha512-pBbrooJMX7795sDcxx1XpwNZC8B/ITyDV+JK2/1qNbQl/1UWqWeh5Dq7qQpMZl5jLdcFDv5IVTM+OhpafSqSFA==", + "version": "0.22.10", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.10.tgz", + "integrity": "sha512-lCaHIJAgTOsplyJzC1w/laxSxrbSsEBw4byKwXgUdMmh+ayPsnidTblenQm+IvhIs44Gcuvlb6pd2LQ0wcKaKg==", "dependencies": { - "@jimp/custom": "^0.22.8", - "@jimp/plugins": "^0.22.8", - "@jimp/types": "^0.22.8", + "@jimp/custom": "^0.22.10", + "@jimp/plugins": "^0.22.10", + "@jimp/types": "^0.22.10", "regenerator-runtime": "^0.13.3" } }, @@ -1912,6 +2487,71 @@ "node": ">=4" } }, + "node_modules/json-colorizer": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/json-colorizer/-/json-colorizer-2.2.2.tgz", + "integrity": "sha512-56oZtwV1piXrQnRNTtJeqRv+B9Y/dXAYLqBBaYl/COcUdoZxgLBLAO88+CnkbT6MxNs0c5E9mPBIb2sFcNz3vw==", + "dependencies": { + "chalk": "^2.4.1", + "lodash.get": "^4.4.2" + } + }, + "node_modules/json-colorizer/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-colorizer/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-colorizer/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/json-colorizer/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/json-colorizer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/json-colorizer/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -1955,6 +2595,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2068,20 +2718,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -2204,6 +2840,17 @@ } } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2253,6 +2900,46 @@ "wrappy": "1" } }, + "node_modules/onnx-proto": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/onnx-proto/-/onnx-proto-4.0.4.tgz", + "integrity": "sha512-aldMOB3HRoo6q/phyB6QRQxSt895HNNw82BNyZ2CMh4bjeKv7g/c+VpAFtJuEMVfYLMbRx61hbuqnKceLeDcDA==", + "dependencies": { + "protobufjs": "^6.8.8" + } + }, + "node_modules/onnxruntime-common": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.14.0.tgz", + "integrity": "sha512-3LJpegM2iMNRX2wUmtYfeX/ytfOzNwAWKSq1HbRrKc9+uqG/FsEA0bbKZl1btQeZaXhC26l44NWpNUeXPII7Ew==" + }, + "node_modules/onnxruntime-node": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.14.0.tgz", + "integrity": "sha512-5ba7TWomIV/9b6NH/1x/8QEeowsb+jBEvFzU6z0T4mNsFwdPqXeFUM7uxC6QeSRkEbWu3qEB0VMjrvzN/0S9+w==", + "optional": true, + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "onnxruntime-common": "~1.14.0" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.14.0.tgz", + "integrity": "sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw==", + "dependencies": { + "flatbuffers": "^1.12.0", + "guid-typescript": "^1.0.9", + "long": "^4.0.0", + "onnx-proto": "^4.0.4", + "onnxruntime-common": "~1.14.0", + "platform": "^1.3.6" + } + }, "node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -2269,6 +2956,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openai": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.3.0.tgz", + "integrity": "sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==", + "dependencies": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + } + }, + "node_modules/openai/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, "node_modules/p-is-promise": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", @@ -2302,23 +3006,34 @@ "xml2js": "^0.4.5" } }, - "node_modules/parse-bmfont-xml/node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/parse-headers": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==" }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2552,6 +3267,11 @@ "node": ">=10" } }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" + }, "node_modules/png-chunk-text": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/png-chunk-text/-/png-chunk-text-1.0.0.tgz", @@ -2630,6 +3350,31 @@ "node": ">=0.4.0" } }, + "node_modules/protobufjs": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2650,6 +3395,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -2899,6 +3649,11 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -2985,6 +3740,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sillytavern-transformers": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/sillytavern-transformers/-/sillytavern-transformers-2.7.3.tgz", + "integrity": "sha512-vr6BQdLlT3TbCLJdzLt5Sc/MzZ7LWoTzdkkQJgtvKwU3sX1TcnW0Oz23hl211sefWdxwkj/g0RZdvL18hk1Jew==", + "dependencies": { + "jimp": "^0.22.10", + "onnxruntime-web": "1.14.0" + }, + "optionalDependencies": { + "onnxruntime-node": "1.14.0" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -3079,6 +3846,11 @@ "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -3165,7 +3937,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3372,6 +4143,14 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3380,6 +4159,24 @@ "node": ">= 0.8" } }, + "node_modules/vectra": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/vectra/-/vectra-0.2.2.tgz", + "integrity": "sha512-Z1d29Zil+dlA9/Qz4VJB3zcWjIARY8QH5xQEnHmGgmTUP58cPRV8NK7P0QH91IRs0RvAWrYiQf9Y5JqQo0vJlQ==", + "dependencies": { + "axios": "^1.3.4", + "cheerio": "^1.0.0-rc.12", + "dotenv": "^8.2.0", + "gpt-3-encoder": "1.1.4", + "json-colorizer": "^2.2.2", + "openai": "^3.2.1", + "uuid": "^9.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "vectra": "bin/vectra.js" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -3391,9 +4188,9 @@ "integrity": "sha512-9kQ9Q/MPzUV2mye8Tv7vA6vDIPk77rI4AWWm2vSaCyGAEsxqyVZYeVU2MSJY5fLkf6u7G5K343vLxKubOxz16Q==" }, "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + "version": "3.6.18", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.18.tgz", + "integrity": "sha512-ltN7j66EneWn5TFDO4L9inYC1D+Czsxlrw2SalgjMmEMkLfA5SIZxEFdE6QtHFiiM6Q7WL32c7AkI3w6yxM84Q==" }, "node_modules/whatwg-url": { "version": "5.0.0", @@ -3474,6 +4271,18 @@ "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==" }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/xmlbuilder": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", diff --git a/package.json b/package.json index 0dc6193d3..bcdf7a8f0 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,8 @@ "@agnai/sentencepiece-js": "^1.1.1", "@agnai/web-tokenizers": "^0.1.3", "@dqbd/tiktoken": "^1.0.2", + "@tensorflow-models/universal-sentence-encoder": "^1.3.3", + "@tensorflow/tfjs": "^4.10.0", "command-exists": "^1.2.9", "compression": "^1", "cookie-parser": "^1.4.6", @@ -15,7 +17,7 @@ "gpt3-tokenizer": "^1.1.5", "ip-matching": "^2.1.2", "ipaddr.js": "^2.0.1", - "jimp": "^0.22.7", + "jimp": "^0.22.10", "jquery": "^3.6.4", "json5": "^2.2.3", "lodash": "^4.17.21", @@ -29,8 +31,10 @@ "png-chunks-extract": "^1.0.0", "response-time": "^2.3.2", "sanitize-filename": "^1.6.3", + "sillytavern-transformers": "^2.7.3", "simple-git": "^3.19.1", "uniqolor": "^1.1.0", + "vectra": "^0.2.2", "webp-converter": "2.3.2", "write-file-atomic": "^5.0.1", "ws": "^8.13.0", @@ -49,7 +53,7 @@ "type": "git", "url": "https://github.com/SillyTavern/SillyTavern.git" }, - "version": "1.10.2", + "version": "1.10.3", "scripts": { "start": "node server.js", "start-multi": "node server.js --disableCsrf", diff --git a/public/script.js b/public/script.js index 13c1d4b86..f6970c2ff 100644 --- a/public/script.js +++ b/public/script.js @@ -121,7 +121,7 @@ import { delay, restoreCaretPosition, saveCaretPosition, - end_trim_to_sentence, + trimToEndSentence, countOccurrences, isOdd, sortMoments, @@ -286,7 +286,9 @@ export const event_types = { CHARACTER_EDITED: 'character_edited', USER_MESSAGE_RENDERED: 'user_message_rendered', CHARACTER_MESSAGE_RENDERED: 'character_message_rendered', - FORCE_SET_BACKGROUND: 'force_set_background,' + FORCE_SET_BACKGROUND: 'force_set_background', + CHAT_DELETED : 'chat_deleted', + GROUP_CHAT_DELETED: 'group_chat_deleted', } export const eventSource = new EventEmitter(); @@ -383,10 +385,7 @@ const system_message_types = { }; const extension_prompt_types = { - /** - * @deprecated Outdated term. In reality it's "after main prompt or story string" - */ - AFTER_SCENARIO: 0, + IN_PROMPT: 0, IN_CHAT: 1 }; @@ -1110,10 +1109,12 @@ async function delChat(chatfile) { }); if (response.ok === true) { // choose another chat if current was deleted - if (chatfile.replace('.jsonl', '') === characters[this_chid].chat) { + const name = chatfile.replace('.jsonl', ''); + if (name === characters[this_chid].chat) { chat_metadata = {}; await replaceCurrentChat(); } + await eventSource.emit(event_types.CHAT_DELETED, name); } } @@ -1148,10 +1149,42 @@ async function replaceCurrentChat() { } } -function printMessages() { - chat.forEach(function (item, i, arr) { - addOneMessage(item, { scroll: i === arr.length - 1 }); - }); +const TRUNCATION_THRESHOLD = 100; + +function showMoreMessages() { + let messageId = Number($('#chat').children('.mes').first().attr('mesid')); + let count = TRUNCATION_THRESHOLD; + + console.debug('Inserting messages before', messageId, 'count', count, 'chat length', chat.length); + const prevHeight = $('#chat').prop('scrollHeight'); + + while(messageId > 0 && count > 0) { + count--; + messageId--; + addOneMessage(chat[messageId], { insertBefore: messageId + 1, scroll: false, forceId: messageId }); + } + + if (messageId == 0) { + $('#show_more_messages').remove(); + } + + const newHeight = $('#chat').prop('scrollHeight'); + $('#chat').scrollTop(newHeight - prevHeight); +} + +async function printMessages() { + let startIndex = 0; + + if (chat.length > TRUNCATION_THRESHOLD) { + count_view_mes = chat.length - TRUNCATION_THRESHOLD; + startIndex = count_view_mes; + $('#chat').append('
Show more messages
'); + } + + for (let i = startIndex; i < chat.length; i++) { + const item = chat[i]; + addOneMessage(item, { scroll: i === chat.length - 1 }); + } if (power_user.lazy_load > 0) { const height = $('#chat').height(); @@ -1194,7 +1227,7 @@ export async function reloadCurrentChat() { } else { resetChatState(); - printMessages(); + await printMessages(); } await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId()); @@ -1430,7 +1463,7 @@ export function addCopyToCodeBlocks(messageElement) { } -function addOneMessage(mes, { type = "normal", insertAfter = null, scroll = true } = {}) { +function addOneMessage(mes, { type = "normal", insertAfter = null, scroll = true, insertBefore = null, forceId = null } = {}) { var messageText = mes["mes"]; const momentDate = timestampToMoment(mes.send_date); const timestamp = momentDate.isValid() ? momentDate.format('LL LT') : ''; @@ -1500,7 +1533,7 @@ function addOneMessage(mes, { type = "normal", insertAfter = null, scroll = true } }*/ let params = { - mesId: count_view_mes, + mesId: forceId ?? count_view_mes, characterName: characterName, isUser: mes.is_user, avatarImg: avatarImg, @@ -1518,18 +1551,31 @@ function addOneMessage(mes, { type = "normal", insertAfter = null, scroll = true const HTMLForEachMes = getMessageFromTemplate(params); if (type !== 'swipe') { - if (!insertAfter) { + if (!insertAfter && !insertBefore) { $("#chat").append(HTMLForEachMes); } - else { + else if (insertAfter) { const target = $("#chat").find(`.mes[mesid="${insertAfter}"]`); $(HTMLForEachMes).insertAfter(target); $(HTMLForEachMes).find('.swipe_left').css('display', 'none'); $(HTMLForEachMes).find('.swipe_right').css('display', 'none'); + } else { + const target = $("#chat").find(`.mes[mesid="${insertBefore}"]`); + $(HTMLForEachMes).insertBefore(target); + $(HTMLForEachMes).find('.swipe_left').css('display', 'none'); + $(HTMLForEachMes).find('.swipe_right').css('display', 'none'); } } - const newMessageId = type == 'swipe' ? count_view_mes - 1 : count_view_mes; + function getMessageId() { + if (typeof forceId == 'number') { + return forceId; + } + + return type == 'swipe' ? count_view_mes - 1 : count_view_mes; + } + + const newMessageId = getMessageId(); const newMessage = $(`#chat [mesid="${newMessageId}"]`); const isSmallSys = mes?.extra?.isSmallSys; newMessage.data("isSystem", isSystem); @@ -1603,6 +1649,11 @@ function addOneMessage(mes, { type = "normal", insertAfter = null, scroll = true swipeMessage.find('.mes_timer').html(''); swipeMessage.find('.tokenCounterDisplay').html(''); } + } else if (typeof forceId == 'number') { + $("#chat").find(`[mesid="${forceId}"]`).find('.mes_text').append(messageText); + appendImageToMessage(mes, newMessage); + hideSwipeButtons(); + showSwipeButtons(); } else { $("#chat").find(`[mesid="${count_view_mes}"]`).find('.mes_text').append(messageText); appendImageToMessage(mes, newMessage); @@ -1613,7 +1664,7 @@ function addOneMessage(mes, { type = "normal", insertAfter = null, scroll = true addCopyToCodeBlocks(newMessage); // Don't scroll if not inserting last - if (!insertAfter && scroll) { + if (!insertAfter && !insertBefore && scroll) { $('#chat .mes').last().addClass('last_mes'); $('#chat .mes').eq(-2).removeClass('last_mes'); @@ -2534,7 +2585,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, addPersonaDescriptionExtensionPrompt(); // Call combined AN into Generate let allAnchors = getAllExtensionPrompts(); - const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO); + const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.IN_PROMPT); let zeroDepthAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, 0, ' '); const storyStringParams = { @@ -3679,7 +3730,13 @@ function setInContextMessages(lastmsg, type) { lastmsg++; } - $('#chat .mes:not([is_system="true"])').eq(-lastmsg).addClass('lastInContext'); + const lastMessageBlock = $('#chat .mes:not([is_system="true"])').eq(-lastmsg); + lastMessageBlock.addClass('lastInContext'); + + if (lastMessageBlock.length === 0) { + const firstMessageId = getFirstDisplayedMessageId(); + $(`#chat .mes[mesid="${firstMessageId}"`).addClass('lastInContext'); + } } function getGenerateUrl() { @@ -3784,7 +3841,7 @@ function cleanUpMessage(getMessage, isImpersonate, isContinue, displayIncomplete getMessage = getRegexedString(getMessage, isImpersonate ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT); if (!displayIncompleteSentences && power_user.trim_sentences) { - getMessage = end_trim_to_sentence(getMessage, power_user.include_newline); + getMessage = trimToEndSentence(getMessage, power_user.include_newline); } if (power_user.collapse_newlines) { @@ -4452,7 +4509,7 @@ async function getChatResult() { chat.push(message); await saveChatConditional(); } - printMessages(); + await printMessages(); select_selected_character(this_chid); await eventSource.emit(event_types.CHAT_CHANGED, (getCurrentChatId())); @@ -5014,12 +5071,12 @@ async function saveSettings(type) { dataType: "json", contentType: "application/json", //processData: false, - success: function (data) { + success: async function (data) { //online_status = data.result; eventSource.emit(event_types.SETTINGS_UPDATED); if (type == "change_name") { clearChat(); - printMessages(); + await printMessages(); } }, error: function (jqXHR, exception) { @@ -5605,7 +5662,7 @@ function select_rm_characters() { * @param {number} position Insertion position. 0 is after story string, 1 is in-chat with custom depth. * @param {number} depth Insertion depth. 0 represets the last message in context. Expected values up to 100. */ -function setExtensionPrompt(key, value, position, depth) { +export function setExtensionPrompt(key, value, position, depth) { extension_prompts[key] = { value: String(value), position: Number(position), depth: Number(depth) }; } @@ -5893,9 +5950,11 @@ async function importCharacterChat(formData) { } function updateViewMessageIds() { + const minId = getFirstDisplayedMessageId(); + $('#chat').find(".mes").each(function (index, element) { - $(element).attr("mesid", index); - $(element).find('.mesIDDisplay').text(`#${index}`); + $(element).attr("mesid", minId + index); + $(element).find('.mesIDDisplay').text(`#${minId + index}`); }); $('#chat .mes').removeClass('last_mes'); @@ -5904,6 +5963,12 @@ function updateViewMessageIds() { updateEditArrowClasses(); } +function getFirstDisplayedMessageId() { + const allIds = Array.from(document.querySelectorAll('#chat .mes')).map(el => Number(el.getAttribute('mesid'))).filter(x => !isNaN(x)); + const minId = Math.min(...allIds); + return minId; +} + function updateEditArrowClasses() { $("#chat .mes .mes_edit_up").removeClass("disabled"); $("#chat .mes .mes_edit_down").removeClass("disabled"); @@ -6306,7 +6371,7 @@ async function createOrEditCharacter(e) { await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1)); clearChat(); - printMessages(); + await printMessages(); await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1)); await saveChatConditional(); } @@ -6893,6 +6958,7 @@ export async function handleDeleteCharacter(popup_type, this_chid, delete_chats) const avatar = characters[this_chid].avatar; const name = characters[this_chid].name; + const pastChats = await getPastCharacterChats(); const msg = { avatar_url: avatar, delete_chats: delete_chats }; @@ -6905,6 +6971,13 @@ export async function handleDeleteCharacter(popup_type, this_chid, delete_chats) if (response.ok) { await deleteCharacter(name, avatar); + + if (delete_chats) { + for (const chat of pastChats) { + const name = chat.file_name.replace('.jsonl', ''); + await eventSource.emit(event_types.CHAT_DELETED, name); + } + } } else { console.error('Failed to delete character: ', response.status, response.statusText); } @@ -6935,7 +7008,7 @@ export async function deleteCharacter(name, avatar) { delete tag_map[avatar]; await getCharacters(); select_rm_info("char_delete", name); - printMessages(); + await printMessages(); saveSettingsDebounced(); } @@ -8835,6 +8908,10 @@ jQuery(async function () { $('#avatar-and-name-block').slideToggle() }); + $(document).on('mouseup touchend', '#show_more_messages', () => { + showMoreMessages(); + }); + // Added here to prevent execution before script.js is loaded and get rid of quirky timeouts await firstLoadInit(); diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 0d0018d17..5e633f17b 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -153,6 +153,8 @@ const extension_settings = { }, speech_recognition: {}, rvc: {}, + hypebot: {}, + vectors: {}, }; let modules = []; diff --git a/public/scripts/extensions/expressions/index.js b/public/scripts/extensions/expressions/index.js index 6026ebf9d..0082637d2 100644 --- a/public/scripts/extensions/expressions/index.js +++ b/public/scripts/extensions/expressions/index.js @@ -3,11 +3,12 @@ import { dragElement, isMobile } from "../../RossAscends-mods.js"; import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplate } from "../../extensions.js"; import { loadMovingUIState, power_user } from "../../power-user.js"; import { registerSlashCommand } from "../../slash-commands.js"; -import { onlyUnique, debounce, getCharaFilename } from "../../utils.js"; +import { onlyUnique, debounce, getCharaFilename, trimToEndSentence, trimToStartSentence } from "../../utils.js"; export { MODULE_NAME }; const MODULE_NAME = 'expressions'; const UPDATE_INTERVAL = 2000; +const STREAMING_UPDATE_INTERVAL = 6000; const FALLBACK_EXPRESSION = 'joy'; const DEFAULT_EXPRESSIONS = [ "talkinghead", @@ -46,6 +47,7 @@ let lastCharacter = undefined; let lastMessage = null; let spriteCache = {}; let inApiCall = false; +let lastServerResponseTime = 0; function isVisualNovelMode() { return Boolean(!isMobile() && power_user.waifuMode && getContext().groupId); @@ -447,7 +449,7 @@ function handleImageChange() { return; } - if (extension_settings.expressions.talkinghead) { + if (extension_settings.expressions.talkinghead && !extension_settings.expressions.local) { // Method get IP of endpoint const talkingheadResultFeedSrc = `${getApiUrl()}/api/talkinghead/result_feed`; $('#expression-holder').css({ display: '' }); @@ -477,6 +479,14 @@ function handleImageChange() { async function moduleWorker() { const context = getContext(); + // Hide and disable talkinghead while in local mode + $('#image_type_block').toggle(!extension_settings.expressions.local); + + if (extension_settings.expressions.local && extension_settings.expressions.talkinghead) { + $('#image_type_toggle').prop('checked', false); + setTalkingHeadState(false); + } + // non-characters not supported if (!context.groupId && (context.characterId === undefined || context.characterId === 'invalid-safety-id')) { removeExpression(); @@ -530,7 +540,7 @@ async function moduleWorker() { } const offlineMode = $('.expression_settings .offline_mode'); - if (!modules.includes('classify')) { + if (!modules.includes('classify') && !extension_settings.expressions.local) { $('.expression_settings').show(); offlineMode.css('display', 'block'); lastCharacter = context.groupId || context.characterId; @@ -566,6 +576,17 @@ async function moduleWorker() { return; } + // Throttle classification requests during streaming + if (context.streamingProcessor && !context.streamingProcessor.isFinished) { + const now = Date.now(); + const timeSinceLastServerResponse = now - lastServerResponseTime; + + if (timeSinceLastServerResponse < STREAMING_UPDATE_INTERVAL) { + console.log('Streaming in progress: throttling expression update. Next update at ' + new Date(lastServerResponseTime + STREAMING_UPDATE_INTERVAL)); + return; + } + } + try { inApiCall = true; let expression = await getExpressionLabel(currentLastMessage.mes); @@ -583,7 +604,6 @@ async function moduleWorker() { } await sendExpressionCall(spriteFolderName, expression, force, vnMode); - } catch (error) { console.log(error); @@ -592,6 +612,7 @@ async function moduleWorker() { inApiCall = false; lastCharacter = context.groupId || context.characterId; lastMessage = currentLastMessage.mes; + lastServerResponseTime = Date.now(); } } @@ -635,6 +656,10 @@ function setTalkingHeadState(switch_var) { extension_settings.expressions.talkinghead = switch_var; // Store setting saveSettingsDebounced(); + if (extension_settings.expressions.local) { + return; + } + talkingHeadCheck().then(result => { if (result) { //console.log("talkinghead exists!"); @@ -709,27 +734,77 @@ async function setSpriteSlashCommand(_, spriteId) { await sendExpressionCall(spriteFolderName, spriteItem.label, true, vnMode); } +/** + * Processes the classification text to reduce the amount of text sent to the API. + * Quotes and asterisks are to be removed. If the text is less than 300 characters, it is returned as is. + * If the text is more than 300 characters, the first and last 150 characters are returned. + * The result is trimmed to the end of sentence. + * @param {string} text The text to process. + * @returns {string} + */ +function sampleClassifyText(text) { + if (!text) { + return text; + } + + // Remove asterisks and quotes + let result = text.replace(/[\*\"]/g, ''); + + const SAMPLE_THRESHOLD = 300; + const HALF_SAMPLE_THRESHOLD = SAMPLE_THRESHOLD / 2; + + if (text.length < SAMPLE_THRESHOLD) { + result = trimToEndSentence(result); + } else { + result = trimToEndSentence(result.slice(0, HALF_SAMPLE_THRESHOLD)) + ' ' + trimToStartSentence(result.slice(-HALF_SAMPLE_THRESHOLD)); + } + + return result.trim(); +} + async function getExpressionLabel(text) { // Return if text is undefined, saving a costly fetch request - if (!modules.includes('classify') || !text) { + if ((!modules.includes('classify') && !extension_settings.expressions.local) || !text) { return FALLBACK_EXPRESSION; } - const url = new URL(getApiUrl()); - url.pathname = '/api/classify'; + text = sampleClassifyText(text); - const apiResult = await doExtrasFetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Bypass-Tunnel-Reminder': 'bypass', - }, - body: JSON.stringify({ text: text }), - }); + try { + if (extension_settings.expressions.local) { + // Local transformers pipeline + const apiResult = await fetch('/api/extra/classify', { + method: 'POST', + headers: getRequestHeaders(), + body: JSON.stringify({ text: text }), + }); - if (apiResult.ok) { - const data = await apiResult.json(); - return data.classification[0].label; + if (apiResult.ok) { + const data = await apiResult.json(); + return data.classification[0].label; + } + } else { + // Extras + const url = new URL(getApiUrl()); + url.pathname = '/api/classify'; + + const apiResult = await doExtrasFetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Bypass-Tunnel-Reminder': 'bypass', + }, + body: JSON.stringify({ text: text }), + }); + + if (apiResult.ok) { + const data = await apiResult.json(); + return data.classification[0].label; + } + } + } catch (error) { + console.log(error); + return FALLBACK_EXPRESSION; } } @@ -821,7 +896,7 @@ async function getSpritesList(name) { async function getExpressionsList() { // get something for offline mode (default images) - if (!modules.includes('classify')) { + if (!modules.includes('classify') && !extension_settings.expressions.local) { return DEFAULT_EXPRESSIONS; } @@ -829,20 +904,34 @@ async function getExpressionsList() { return expressionsList; } - const url = new URL(getApiUrl()); - url.pathname = '/api/classify/labels'; try { - const apiResult = await doExtrasFetch(url, { - method: 'GET', - headers: { 'Bypass-Tunnel-Reminder': 'bypass' }, - }); + if (extension_settings.expressions.local) { + const apiResult = await fetch('/api/extra/classify/labels', { + method: 'POST', + headers: getRequestHeaders(), + }); - if (apiResult.ok) { + if (apiResult.ok) { + const data = await apiResult.json(); + expressionsList = data.labels; + return expressionsList; + } + } else { + const url = new URL(getApiUrl()); + url.pathname = '/api/classify/labels'; - const data = await apiResult.json(); - expressionsList = data.labels; - return expressionsList; + const apiResult = await doExtrasFetch(url, { + method: 'GET', + headers: { 'Bypass-Tunnel-Reminder': 'bypass' }, + }); + + if (apiResult.ok) { + + const data = await apiResult.json(); + expressionsList = data.labels; + return expressionsList; + } } } catch (error) { @@ -852,7 +941,7 @@ async function getExpressionsList() { } async function setExpression(character, expression, force) { - if (!extension_settings.expressions.talkinghead) { + if (extension_settings.expressions.local || !extension_settings.expressions.talkinghead) { console.debug('entered setExpressions'); await validateImages(character); const img = $('img.expression'); @@ -1226,6 +1315,11 @@ function setExpressionOverrideHtml(forceClear = false) { $('#expressions_show_default').on('input', onExpressionsShowDefaultInput); $('#expression_upload_pack_button').on('click', onClickExpressionUploadPackButton); $('#expressions_show_default').prop('checked', extension_settings.expressions.showDefault).trigger('input'); + $('#expression_local').prop('checked', extension_settings.expressions.local).on('input', function () { + extension_settings.expressions.local = !!$(this).prop('checked'); + moduleWorker(); + saveSettingsDebounced(); + }); $('#expression_override_cleanup_button').on('click', onClickExpressionOverrideRemoveAllButton); $(document).on('dragstart', '.expression', (e) => { e.preventDefault() diff --git a/public/scripts/extensions/expressions/settings.html b/public/scripts/extensions/expressions/settings.html index 27779cce2..5ecd2e971 100644 --- a/public/scripts/extensions/expressions/settings.html +++ b/public/scripts/extensions/expressions/settings.html @@ -6,14 +6,14 @@
- -
- -
+ +
You are in offline mode. Click on the image below to set the expression.
diff --git a/public/scripts/extensions/infinity-context/index.js b/public/scripts/extensions/infinity-context/index.js index b06309e5b..303b2eee0 100644 --- a/public/scripts/extensions/infinity-context/index.js +++ b/public/scripts/extensions/infinity-context/index.js @@ -739,7 +739,7 @@ window.chromadb_interceptGeneration = async (chat, maxContext) => { // No memories? No prompt. const promptBlob = (tokenApprox == 0) ? "" : wrapperMsg.replace('{{memories}}', allMemoryBlob); console.debug("CHROMADB: prompt blob: %o", promptBlob); - context.setExtensionPrompt(MODULE_NAME, promptBlob, extension_prompt_types.AFTER_SCENARIO); + context.setExtensionPrompt(MODULE_NAME, promptBlob, extension_prompt_types.IN_PROMPT); } if (selectedStrategy === 'custom') { const context = getContext(); diff --git a/public/scripts/extensions/memory/index.js b/public/scripts/extensions/memory/index.js index 8dcf841d5..d2eaebf4f 100644 --- a/public/scripts/extensions/memory/index.js +++ b/public/scripts/extensions/memory/index.js @@ -63,7 +63,7 @@ const defaultSettings = { source: summary_sources.extras, prompt: defaultPrompt, template: defaultTemplate, - position: extension_prompt_types.AFTER_SCENARIO, + position: extension_prompt_types.IN_PROMPT, depth: 2, promptWords: 200, promptMinWords: 25, diff --git a/public/scripts/extensions/translate/index.js b/public/scripts/extensions/translate/index.js index 488ab2071..dd20a7b29 100644 --- a/public/scripts/extensions/translate/index.js +++ b/public/scripts/extensions/translate/index.js @@ -135,8 +135,8 @@ const languageCodes = { 'Zulu': 'zu', }; -const KEY_REQUIRED = ['deepl','libre']; -const LOCAL_URL = ['libre']; +const KEY_REQUIRED = ['deepl', 'libre']; +const LOCAL_URL = ['libre', 'oneringtranslator', 'deeplx']; function showKeysButton() { const providerRequiresKey = KEY_REQUIRED.includes(extension_settings.translate.provider); @@ -144,6 +144,7 @@ function showKeysButton() { $("#translate_key_button").toggle(providerRequiresKey); $("#translate_key_button").toggleClass('success', Boolean(secret_state[extension_settings.translate.provider])); $("#translate_url_button").toggle(providerOptionalUrl); + $("#translate_url_button").toggleClass('success', Boolean(secret_state[extension_settings.translate.provider + "_url"])); } function loadSettings() { @@ -184,8 +185,33 @@ async function translateIncomingMessage(messageId) { updateMessageBlock(messageId, message); } +async function translateProviderOneRing(text, lang) { + let from_lang = lang == extension_settings.translate.internal_language + ? extension_settings.translate.target_language + : extension_settings.translate.internal_language; + + const response = await fetch('/api/translate/onering', { + method: 'POST', + headers: getRequestHeaders(), + body: JSON.stringify({ text: text, from_lang: from_lang, to_lang: lang }), + }); + + if (response.ok) { + const result = await response.text(); + return result; + } + + throw new Error(response.statusText); +} + +/** + * Translates text using the LibreTranslate API + * @param {string} text Text to translate + * @param {string} lang Target language code + * @returns {Promise} Translated text + */ async function translateProviderLibre(text, lang) { - const response = await fetch('/libre_translate', { + const response = await fetch('/api/translate/libre', { method: 'POST', headers: getRequestHeaders(), body: JSON.stringify({ text: text, lang: lang }), @@ -199,8 +225,14 @@ async function translateProviderLibre(text, lang) { throw new Error(response.statusText); } +/** + * Translates text using the Google Translate API + * @param {string} text Text to translate + * @param {string} lang Target language code + * @returns {Promise} Translated text + */ async function translateProviderGoogle(text, lang) { - const response = await fetch('/google_translate', { + const response = await fetch('/api/translate/google', { method: 'POST', headers: getRequestHeaders(), body: JSON.stringify({ text: text, lang: lang }), @@ -214,12 +246,18 @@ async function translateProviderGoogle(text, lang) { throw new Error(response.statusText); } +/** + * Translates text using the DeepL API + * @param {string} text Text to translate + * @param {string} lang Target language code + * @returns {Promise} Translated text + */ async function translateProviderDeepl(text, lang) { if (!secret_state.deepl) { throw new Error('No DeepL API key'); } - const response = await fetch('/deepl_translate', { + const response = await fetch('/api/translate/deepl', { method: 'POST', headers: getRequestHeaders(), body: JSON.stringify({ text: text, lang: lang }), @@ -233,6 +271,33 @@ async function translateProviderDeepl(text, lang) { throw new Error(response.statusText); } +/** + * Translates text using the DeepLX API + * @param {string} text Text to translate + * @param {string} lang Target language code + * @returns {Promise} Translated text + */ +async function translateProviderDeepLX(text, lang) { + const response = await fetch('/api/translate/deeplx', { + method: 'POST', + headers: getRequestHeaders(), + body: JSON.stringify({ text: text, lang: lang }), + }); + + if (response.ok) { + const result = await response.text(); + return result; + } + + throw new Error(response.statusText); +} + +/** + * Translates text using the selected translation provider + * @param {string} text Text to translate + * @param {string} lang Target language code + * @returns {Promise} Translated text + */ async function translate(text, lang) { try { if (text == '') { @@ -246,6 +311,10 @@ async function translate(text, lang) { return await translateProviderGoogle(text, lang); case 'deepl': return await translateProviderDeepl(text, lang); + case 'deeplx': + return await translateProviderDeepLX(text, lang); + case 'oneringtranslator': + return await translateProviderOneRing(text, lang); default: console.error('Unknown translation provider', extension_settings.translate.provider); return text; @@ -391,6 +460,8 @@ jQuery(() => { + + + Enabled + + + +
+ + + +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + Old messages are vectorized gradually as you chat. + To process all previous messages, click the button below. + + + +
+ + diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index bb2439280..ddac1ef48 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -166,7 +166,7 @@ export async function getGroupChat(groupId) { for (let key of data) { chat.push(key); } - printMessages(); + await printMessages(); } else { sendSystemMessage(system_message_types.GROUP, '', { isSmallSys: true }); if (group && Array.isArray(group.members)) { @@ -816,18 +816,26 @@ function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, i } async function deleteGroup(id) { + const group = groups.find((x) => x.id === id); + const response = await fetch("/deletegroup", { method: "POST", headers: getRequestHeaders(), body: JSON.stringify({ id: id }), }); + if (group && Array.isArray(group.chats)) { + for (const chatId of group.chats) { + await eventSource.emit(event_types.GROUP_CHAT_DELETED, chatId); + } + } + if (response.ok) { selected_group = null; delete tag_map[id]; resetChatState(); clearChat(); - printMessages(); + await printMessages(); await getCharacters(); select_rm_info("group_delete", id); @@ -1493,6 +1501,8 @@ export async function deleteGroupChat(groupId, chatId) { } else { await createNewGroupChat(groupId); } + + await eventSource.emit(event_types.GROUP_CHAT_DELETED, chatId); } } diff --git a/public/scripts/openai.js b/public/scripts/openai.js index d4764f02b..b1faed662 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -630,6 +630,12 @@ function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, ty if (true === afterScenario) chatCompletion.insert(authorsNote, 'scenario'); } + // Vectors Memory + if (prompts.has('vectorsMemory')) { + const vectorsMemory = Message.fromPrompt(prompts.get('vectorsMemory')); + chatCompletion.insert(vectorsMemory, 'main'); + } + // Decide whether dialogue examples should always be added if (power_user.pin_examples) { populateDialogueExamples(prompts, chatCompletion); @@ -697,6 +703,14 @@ function preparePromptsForChatCompletion({Scenario, charPersonality, name2, worl identifier: 'authorsNote' }); + // Vectors Memory + const vectorsMemory = extensionPrompts['3_vectors']; + if (vectorsMemory && vectorsMemory.value) systemPrompts.push({ + role: 'system', + content: vectorsMemory.value, + identifier: 'vectorsMemory', + }); + // Persona Description if (power_user.persona_description && power_user.persona_description_position === persona_description_positions.IN_PROMPT) { systemPrompts.push({ role: 'system', content: power_user.persona_description, identifier: 'personaDescription' }); diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 121c97334..6ea5fc667 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -438,9 +438,9 @@ export function sortByCssOrder(a, b) { * @param {boolean} include_newline Whether to include a newline character in the trimmed string. * @returns {string} The trimmed string. * @example - * end_trim_to_sentence('Hello, world! I am from'); // 'Hello, world!' + * trimToEndSentence('Hello, world! I am from'); // 'Hello, world!' */ -export function end_trim_to_sentence(input, include_newline = false) { +export function trimToEndSentence(input, include_newline = false) { const punctuation = new Set(['.', '!', '?', '*', '"', ')', '}', '`', ']', '$', '。', '!', '?', '”', ')', '】', '】', '’', '」', '】']); // extend this as you see fit let last = -1; @@ -465,6 +465,26 @@ export function end_trim_to_sentence(input, include_newline = false) { return input.substring(0, last + 1).trimEnd(); } +export function trimToStartSentence(input) { + let p1 = input.indexOf("."); + let p2 = input.indexOf("!"); + let p3 = input.indexOf("?"); + let p4 = input.indexOf("\n"); + let first = p1; + let skip1 = false; + if (p2 > 0 && p2 < first) { first = p2; } + if (p3 > 0 && p3 < first) { first = p3; } + if (p4 > 0 && p4 < first) { first = p4; skip1 = true; } + if (first > 0) { + if (skip1) { + return input.substring(first + 1); + } else { + return input.substring(first + 2); + } + } + return input; +} + /** * Counts the number of occurrences of a character in a string. * @param {string} string The string to count occurrences in. diff --git a/public/style.css b/public/style.css index 65a947656..71b0ef5a7 100644 --- a/public/style.css +++ b/public/style.css @@ -1209,6 +1209,7 @@ input[type="file"] { .radio_group { display: flex; flex-direction: column; + margin-top: 5px; } #extension_floating_counter { @@ -3580,6 +3581,15 @@ a { align-self: center; } +#show_more_messages { + text-align: center; + margin: 10px 0; + font-weight: 500; + text-decoration: underline; + order: -1; + cursor: pointer; +} + #select_chat_search { background-color: transparent; border: none; diff --git a/server.js b/server.js index bd73238b8..80c47884e 100644 --- a/server.js +++ b/server.js @@ -58,7 +58,6 @@ const { Tokenizer } = require('@agnai/web-tokenizers'); // misc/other imports const _ = require('lodash'); -const { generateRequestUrl, normaliseResponse } = require('google-translate-api-browser'); // Unrestrict console logs display limit util.inspect.defaultOptions.maxArrayLength = null; @@ -74,6 +73,7 @@ const characterCardParser = require('./src/character-card-parser.js'); const contentManager = require('./src/content-manager'); const novelai = require('./src/novelai'); const statsHelpers = require('./statsHelpers.js'); +const { writeSecret, readSecret, readSecretState, migrateSecrets, SECRET_KEYS, getAllSecrets } = require('./src/secrets'); function createDefaultFiles() { const files = { @@ -338,6 +338,7 @@ function humanizedISO8601DateTime(date) { var charactersPath = 'public/characters/'; var chatsPath = 'public/chats/'; const UPLOADS_PATH = './uploads'; +const SETTINGS_FILE = './public/settings.json'; const AVATAR_WIDTH = 400; const AVATAR_HEIGHT = 600; const jsonParser = express.json({ limit: '100mb' }); @@ -4112,7 +4113,7 @@ const setupTasks = async function () { console.log(`SillyTavern ${version.pkgVersion}` + (version.gitBranch ? ` '${version.gitBranch}' (${version.gitRevision})` : '')); backupSettings(); - migrateSecrets(); + migrateSecrets(SETTINGS_FILE); ensurePublicDirectoriesExist(); await ensureThumbnailCache(); contentManager.checkForNewContent(); @@ -4263,69 +4264,6 @@ function ensurePublicDirectoriesExist() { } } -const SECRETS_FILE = './secrets.json'; -const SETTINGS_FILE = './public/settings.json'; -const SECRET_KEYS = { - HORDE: 'api_key_horde', - MANCER: 'api_key_mancer', - OPENAI: 'api_key_openai', - NOVEL: 'api_key_novel', - CLAUDE: 'api_key_claude', - DEEPL: 'deepl', - LIBRE: 'libre', - LIBRE_URL: 'libre_url', - OPENROUTER: 'api_key_openrouter', - SCALE: 'api_key_scale', - AI21: 'api_key_ai21', - SCALE_COOKIE: 'scale_cookie', -} - -function migrateSecrets() { - if (!fs.existsSync(SETTINGS_FILE)) { - console.log('Settings file does not exist'); - return; - } - - try { - let modified = false; - const fileContents = fs.readFileSync(SETTINGS_FILE, 'utf8'); - const settings = JSON.parse(fileContents); - const oaiKey = settings?.api_key_openai; - const hordeKey = settings?.horde_settings?.api_key; - const novelKey = settings?.api_key_novel; - - if (typeof oaiKey === 'string') { - console.log('Migrating OpenAI key...'); - writeSecret(SECRET_KEYS.OPENAI, oaiKey); - delete settings.api_key_openai; - modified = true; - } - - if (typeof hordeKey === 'string') { - console.log('Migrating Horde key...'); - writeSecret(SECRET_KEYS.HORDE, hordeKey); - delete settings.horde_settings.api_key; - modified = true; - } - - if (typeof novelKey === 'string') { - console.log('Migrating Novel key...'); - writeSecret(SECRET_KEYS.NOVEL, novelKey); - delete settings.api_key_novel; - modified = true; - } - - if (modified) { - console.log('Writing updated settings.json...'); - const settingsContent = JSON.stringify(settings); - writeFileAtomicSync(SETTINGS_FILE, settingsContent, "utf-8"); - } - } - catch (error) { - console.error('Could not migrate secrets file. Proceed with caution.'); - } -} - app.post('/writesecret', jsonParser, (request, response) => { const key = request.body.key; const value = request.body.value; @@ -4335,19 +4273,9 @@ app.post('/writesecret', jsonParser, (request, response) => { }); app.post('/readsecretstate', jsonParser, (_, response) => { - if (!fs.existsSync(SECRETS_FILE)) { - return response.send({}); - } try { - const fileContents = fs.readFileSync(SECRETS_FILE, 'utf8'); - const secrets = JSON.parse(fileContents); - const state = {}; - - for (const key of Object.values(SECRET_KEYS)) { - state[key] = !!secrets[key]; // convert to boolean - } - + const state = readSecretState(); return response.send(state); } catch (error) { console.error(error); @@ -4393,14 +4321,13 @@ app.post('/viewsecrets', jsonParser, async (_, response) => { return response.sendStatus(403); } - if (!fs.existsSync(SECRETS_FILE)) { - console.error('secrets.json does not exist'); - return response.sendStatus(404); - } - try { - const fileContents = fs.readFileSync(SECRETS_FILE, 'utf-8'); - const secrets = JSON.parse(fileContents); + const secrets = getAllSecrets(); + + if (!secrets) { + return response.sendStatus(404); + } + return response.send(secrets); } catch (error) { console.error(error); @@ -4835,122 +4762,6 @@ app.post('/api/sd/generate', jsonParser, async (request, response) => { } }); -app.post('/libre_translate', jsonParser, async (request, response) => { - const key = readSecret(SECRET_KEYS.LIBRE); - const url = readSecret(SECRET_KEYS.LIBRE_URL); - - const text = request.body.text; - const lang = request.body.lang; - - if (!text || !lang) { - return response.sendStatus(400); - } - - console.log('Input text: ' + text); - - try { - const result = await fetch(url, { - method: "POST", - body: JSON.stringify({ - q: text, - source: "auto", - target: lang, - format: "text", - api_key: key - }), - headers: { "Content-Type": "application/json" } - }); - - if (!result.ok) { - return response.sendStatus(result.status); - } - - const json = await result.json(); - console.log('Translated text: ' + json.translatedText); - - return response.send(json.translatedText); - } catch (error) { - console.log("Translation error: " + error.message); - return response.sendStatus(500); - } -}); - -app.post('/google_translate', jsonParser, async (request, response) => { - const text = request.body.text; - const lang = request.body.lang; - - if (!text || !lang) { - return response.sendStatus(400); - } - - console.log('Input text: ' + text); - - const url = generateRequestUrl(text, { to: lang }); - - https.get(url, (resp) => { - let data = ''; - - resp.on('data', (chunk) => { - data += chunk; - }); - - resp.on('end', () => { - const result = normaliseResponse(JSON.parse(data)); - console.log('Translated text: ' + result.text); - return response.send(result.text); - }); - }).on("error", (err) => { - console.log("Translation error: " + err.message); - return response.sendStatus(500); - }); -}); - -app.post('/deepl_translate', jsonParser, async (request, response) => { - const key = readSecret(SECRET_KEYS.DEEPL); - - if (!key) { - return response.sendStatus(401); - } - - const text = request.body.text; - const lang = request.body.lang; - - if (!text || !lang) { - return response.sendStatus(400); - } - - console.log('Input text: ' + text); - - const params = new URLSearchParams(); - params.append('text', text); - params.append('target_lang', lang); - - try { - const result = await fetch('https://api-free.deepl.com/v2/translate', { - method: 'POST', - body: params, - headers: { - 'Accept': 'application/json', - 'Authorization': `DeepL-Auth-Key ${key}`, - 'Content-Type': 'application/x-www-form-urlencoded', - }, - timeout: 0, - }); - - if (!result.ok) { - return response.sendStatus(result.status); - } - - const json = await result.json(); - console.log('Translated text: ' + json.translations[0].text); - - return response.send(json.translations[0].text); - } catch (error) { - console.log("Translation error: " + error.message); - return response.sendStatus(500); - } -}); - app.post('/novel_tts', jsonParser, async (request, response) => { const token = readSecret(SECRET_KEYS.NOVEL); @@ -5334,27 +5145,6 @@ function importRisuSprites(data) { } } -function writeSecret(key, value) { - if (!fs.existsSync(SECRETS_FILE)) { - const emptyFile = JSON.stringify({}); - writeFileAtomicSync(SECRETS_FILE, emptyFile, "utf-8"); - } - - const fileContents = fs.readFileSync(SECRETS_FILE, 'utf-8'); - const secrets = JSON.parse(fileContents); - secrets[key] = value; - writeFileAtomicSync(SECRETS_FILE, JSON.stringify(secrets), "utf-8"); -} - -function readSecret(key) { - if (!fs.existsSync(SECRETS_FILE)) { - return undefined; - } - - const fileContents = fs.readFileSync(SECRETS_FILE, 'utf-8'); - const secrets = JSON.parse(fileContents); - return secrets[key]; -} async function readAllChunks(readableStream) { return new Promise((resolve, reject) => { @@ -5427,8 +5217,6 @@ async function getImageBuffers(zipFilePath) { }); } - - /** * This function extracts the extension information from the manifest file. * @param {string} extensionPath - The path of the extension folder @@ -5867,3 +5655,20 @@ app.post('/get_character_assets_list', jsonParser, async (request, response) => return response.sendStatus(500); } }); + +// Vector storage DB +require('./src/vectors').registerEndpoints(app, jsonParser); +// Chat translation +require('./src/translate').registerEndpoints(app, jsonParser); +// Emotion classification +import('./src/classify.mjs').then(module => { + module.default.registerEndpoints(app, jsonParser); +}).catch(err => { + console.error(err); +}); +// Image captioning +import('./src/caption.mjs').then(module => { + module.default.registerEndpoints(app, jsonParser); +}).catch(err => { + console.error(err); +}); diff --git a/src/caption.mjs b/src/caption.mjs new file mode 100644 index 000000000..08bcd25b5 --- /dev/null +++ b/src/caption.mjs @@ -0,0 +1,70 @@ +import { pipeline, env, RawImage } from 'sillytavern-transformers'; +import path from 'path'; +import { getConfig } from './util.js'; + +// Limit the number of threads to 1 to avoid issues on Android +env.backends.onnx.wasm.numThreads = 1; + +class PipelineAccessor { + /** + * @type {import("sillytavern-transformers").ImageToTextPipeline} + */ + pipe; + + async get() { + if (!this.pipe) { + const cache_dir = path.join(process.cwd(), 'cache'); + const model = this.getCaptioningModel(); + this.pipe = await pipeline('image-to-text', model, { cache_dir, quantized: true }); + } + + return this.pipe; + } + + getCaptioningModel() { + const DEFAULT_MODEL = 'Xenova/vit-gpt2-image-captioning'; + + try { + const config = getConfig(); + const model = config?.extras?.captioningModel; + return model || DEFAULT_MODEL; + } catch (error) { + console.warn('Failed to read config.conf, using default captioning model.'); + return DEFAULT_MODEL; + } + } +} + +/** + * @param {import("express").Express} app + * @param {any} jsonParser + */ +function registerEndpoints(app, jsonParser) { + const pipelineAccessor = new PipelineAccessor(); + + app.post('/api/extra/caption', jsonParser, async (req, res) => { + try { + const { image } = req.body; + + // base64 string to blob + const buffer = Buffer.from(image, 'base64'); + const byteArray = new Uint8Array(buffer); + const blob = new Blob([byteArray]); + + const rawImage = await RawImage.fromBlob(blob); + const pipe = await pipelineAccessor.get(); + const result = await pipe(rawImage); + const text = result[0].generated_text; + console.log('Image caption:', text); + + return res.json({ caption: text }); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } + }); +} + +export default { + registerEndpoints, +}; diff --git a/src/classify.mjs b/src/classify.mjs new file mode 100644 index 000000000..df2baca75 --- /dev/null +++ b/src/classify.mjs @@ -0,0 +1,87 @@ +import { pipeline, env } from 'sillytavern-transformers'; +import path from 'path'; +import { getConfig } from './util.js'; + +// Limit the number of threads to 1 to avoid issues on Android +env.backends.onnx.wasm.numThreads = 1; + +class PipelineAccessor { + /** + * @type {import("sillytavern-transformers").TextClassificationPipeline} + */ + pipe; + + async get() { + if (!this.pipe) { + const cache_dir = path.join(process.cwd(), 'cache'); + const model = this.getClassificationModel(); + this.pipe = await pipeline('text-classification', model, { cache_dir, quantized: true }); + } + + return this.pipe; + } + + getClassificationModel() { + const DEFAULT_MODEL = 'Cohee/distilbert-base-uncased-go-emotions-onnx'; + + try { + const config = getConfig(); + const model = config?.extras?.classificationModel; + return model || DEFAULT_MODEL; + } catch (error) { + console.warn('Failed to read config.conf, using default classification model.'); + return DEFAULT_MODEL; + } + } +} + +/** + * @param {import("express").Express} app + * @param {any} jsonParser + */ +function registerEndpoints(app, jsonParser) { + const cacheObject = {}; + const pipelineAccessor = new PipelineAccessor(); + + app.post('/api/extra/classify/labels', jsonParser, async (req, res) => { + try { + const pipe = await pipelineAccessor.get(); + const result = Object.keys(pipe.model.config.label2id); + return res.json({ labels: result }); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } + }); + + app.post('/api/extra/classify', jsonParser, async (req, res) => { + try { + const { text } = req.body; + + async function getResult(text) { + if (cacheObject.hasOwnProperty(text)) { + return cacheObject[text]; + } else { + const pipe = await pipelineAccessor.get(); + const result = await pipe(text, { topk: 5 }); + result.sort((a, b) => b.score - a.score); + cacheObject[text] = result; + return result; + } + } + + console.log('Classify input:', text); + const result = await getResult(text); + console.log('Classify output:', result); + + return res.json({ classification: result }); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } + }); +} + +export default { + registerEndpoints, +}; diff --git a/src/local-vectors.js b/src/local-vectors.js new file mode 100644 index 000000000..73d514840 --- /dev/null +++ b/src/local-vectors.js @@ -0,0 +1,38 @@ + +require('@tensorflow/tfjs'); +const encoder = require('@tensorflow-models/universal-sentence-encoder'); + +/** + * Lazy loading class for the embedding model. + */ +class EmbeddingModel { + /** + * @type {encoder.UniversalSentenceEncoder} - The embedding model + */ + model; + + async get() { + if (!this.model) { + this.model = await encoder.load(); + } + + return this.model; + } +} + +const model = new EmbeddingModel(); + +/** + * @param {string} text + */ +async function getLocalVector(text) { + const use = await model.get(); + const tensor = await use.embed(text); + const vector = Array.from(await tensor.data()); + + return vector; +} + +module.exports = { + getLocalVector, +}; diff --git a/src/middleware/basicAuthMiddleware.js b/src/middleware/basicAuthMiddleware.js index a5b1b3459..25036ca1e 100644 --- a/src/middleware/basicAuthMiddleware.js +++ b/src/middleware/basicAuthMiddleware.js @@ -2,12 +2,7 @@ * When applied, this middleware will ensure the request contains the required header for basic authentication and only * allow access to the endpoint after successful authentication. */ - -//const {dirname} = require('path'); -//const appDir = dirname(require.main.filename); -//const config = require(appDir + '/config.conf'); -const path = require('path'); -const config = require(path.join(process.cwd(), './config.conf')); +const { getConfig } = require('./../util.js'); const unauthorizedResponse = (res) => { res.set('WWW-Authenticate', 'Basic realm="SillyTavern", charset="UTF-8"'); @@ -15,6 +10,7 @@ const unauthorizedResponse = (res) => { }; const basicAuthMiddleware = function (request, response, callback) { + const config = getConfig(); const authHeader = request.headers.authorization; if (!authHeader) { diff --git a/src/openai-vectors.js b/src/openai-vectors.js new file mode 100644 index 000000000..b7eb89162 --- /dev/null +++ b/src/openai-vectors.js @@ -0,0 +1,48 @@ +const fetch = require('node-fetch').default; +const { SECRET_KEYS, readSecret } = require('./secrets'); + +/** + * Gets the vector for the given text from OpenAI ada model + * @param {string} text - The text to get the vector for + * @returns {Promise} - The vector for the text + */ +async function getOpenAIVector(text) { + const key = readSecret(SECRET_KEYS.OPENAI); + + if (!key) { + console.log('No OpenAI key found'); + throw new Error('No OpenAI key found'); + } + + const response = await fetch('https://api.openai.com/v1/embeddings', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${key}`, + }, + body: JSON.stringify({ + input: text, + model: 'text-embedding-ada-002', + }) + }); + + if (!response.ok) { + const text = await response.text(); + console.log('OpenAI request failed', response.statusText, text); + throw new Error('OpenAI request failed'); + } + + const data = await response.json(); + const vector = data?.data[0]?.embedding; + + if (!Array.isArray(vector)) { + console.log('OpenAI response was not an array'); + throw new Error('OpenAI response was not an array'); + } + + return vector; +} + +module.exports = { + getOpenAIVector, +}; diff --git a/src/secrets.js b/src/secrets.js new file mode 100644 index 000000000..b09a33f08 --- /dev/null +++ b/src/secrets.js @@ -0,0 +1,148 @@ +const fs = require('fs'); +const path = require('path'); +const writeFileAtomicSync = require('write-file-atomic').sync; + +const SECRETS_FILE = path.join(process.cwd(), './secrets.json'); +const SECRET_KEYS = { + HORDE: 'api_key_horde', + MANCER: 'api_key_mancer', + OPENAI: 'api_key_openai', + NOVEL: 'api_key_novel', + CLAUDE: 'api_key_claude', + DEEPL: 'deepl', + LIBRE: 'libre', + LIBRE_URL: 'libre_url', + OPENROUTER: 'api_key_openrouter', + SCALE: 'api_key_scale', + AI21: 'api_key_ai21', + SCALE_COOKIE: 'scale_cookie', + ONERING_URL: 'oneringtranslator_url', + DEEPLX_URL: 'deeplx_url', +} + +/** + * Writes a secret to the secrets file + * @param {string} key Secret key + * @param {string} value Secret value + */ +function writeSecret(key, value) { + if (!fs.existsSync(SECRETS_FILE)) { + const emptyFile = JSON.stringify({}); + writeFileAtomicSync(SECRETS_FILE, emptyFile, "utf-8"); + } + + const fileContents = fs.readFileSync(SECRETS_FILE, 'utf-8'); + const secrets = JSON.parse(fileContents); + secrets[key] = value; + writeFileAtomicSync(SECRETS_FILE, JSON.stringify(secrets), "utf-8"); +} + +/** + * Reads a secret from the secrets file + * @param {string} key Secret key + * @returns {string} Secret value + */ +function readSecret(key) { + if (!fs.existsSync(SECRETS_FILE)) { + return ''; + } + + const fileContents = fs.readFileSync(SECRETS_FILE, 'utf-8'); + const secrets = JSON.parse(fileContents); + return secrets[key]; +} + +/** + * Reads the secret state from the secrets file + * @returns {object} Secret state + */ +function readSecretState() { + if (!fs.existsSync(SECRETS_FILE)) { + return {}; + } + + const fileContents = fs.readFileSync(SECRETS_FILE, 'utf8'); + const secrets = JSON.parse(fileContents); + const state = {}; + + for (const key of Object.values(SECRET_KEYS)) { + state[key] = !!secrets[key]; // convert to boolean + } + + return state; +} + +/** + * Migrates secrets from settings.json to secrets.json + * @param {string} settingsFile Path to settings.json + * @returns {void} + */ +function migrateSecrets(settingsFile) { + if (!fs.existsSync(settingsFile)) { + console.log('Settings file does not exist'); + return; + } + + try { + let modified = false; + const fileContents = fs.readFileSync(settingsFile, 'utf8'); + const settings = JSON.parse(fileContents); + const oaiKey = settings?.api_key_openai; + const hordeKey = settings?.horde_settings?.api_key; + const novelKey = settings?.api_key_novel; + + if (typeof oaiKey === 'string') { + console.log('Migrating OpenAI key...'); + writeSecret(SECRET_KEYS.OPENAI, oaiKey); + delete settings.api_key_openai; + modified = true; + } + + if (typeof hordeKey === 'string') { + console.log('Migrating Horde key...'); + writeSecret(SECRET_KEYS.HORDE, hordeKey); + delete settings.horde_settings.api_key; + modified = true; + } + + if (typeof novelKey === 'string') { + console.log('Migrating Novel key...'); + writeSecret(SECRET_KEYS.NOVEL, novelKey); + delete settings.api_key_novel; + modified = true; + } + + if (modified) { + console.log('Writing updated settings.json...'); + const settingsContent = JSON.stringify(settings); + writeFileAtomicSync(settingsFile, settingsContent, "utf-8"); + } + } + catch (error) { + console.error('Could not migrate secrets file. Proceed with caution.'); + } +} + +/** + * Reads all secrets from the secrets file + * @returns {Record | undefined} Secrets + */ +function getAllSecrets() { + if (!fs.existsSync(SECRETS_FILE)) { + console.log('Secrets file does not exist'); + return undefined; + } + + const fileContents = fs.readFileSync(SECRETS_FILE, 'utf8'); + const secrets = JSON.parse(fileContents); + return secrets; +} + +module.exports = { + writeSecret, + readSecret, + readSecretState, + migrateSecrets, + getAllSecrets, + SECRET_KEYS, +}; diff --git a/src/translate.js b/src/translate.js new file mode 100644 index 000000000..a8f6056f0 --- /dev/null +++ b/src/translate.js @@ -0,0 +1,248 @@ +const fetch = require('node-fetch').default; +const https = require('https'); +const { readSecret, SECRET_KEYS } = require('./secrets'); +const { generateRequestUrl, normaliseResponse } = require('google-translate-api-browser'); + +const DEEPLX_URL_DEFAULT = 'http://127.0.0.1:1188/translate'; +const ONERING_URL_DEFAULT = 'http://127.0.0.1:4990/translate'; + +/** + * @param {import("express").Express} app + * @param {any} jsonParser + */ +function registerEndpoints(app, jsonParser) { + app.post('/api/translate/libre', jsonParser, async (request, response) => { + const key = readSecret(SECRET_KEYS.LIBRE); + const url = readSecret(SECRET_KEYS.LIBRE_URL); + + if (!url) { + console.log('LibreTranslate URL is not configured.'); + return response.sendStatus(401); + } + + const text = request.body.text; + const lang = request.body.lang; + + if (!text || !lang) { + return response.sendStatus(400); + } + + console.log('Input text: ' + text); + + try { + const result = await fetch(url, { + method: "POST", + body: JSON.stringify({ + q: text, + source: "auto", + target: lang, + format: "text", + api_key: key + }), + headers: { "Content-Type": "application/json" } + }); + + if (!result.ok) { + const error = await result.text(); + console.log('LibreTranslate error: ', result.statusText, error); + return response.sendStatus(result.status); + } + + const json = await result.json(); + console.log('Translated text: ' + json.translatedText); + + return response.send(json.translatedText); + } catch (error) { + console.log("Translation error: " + error.message); + return response.sendStatus(500); + } + }); + + app.post('/api/translate/google', jsonParser, async (request, response) => { + const text = request.body.text; + const lang = request.body.lang; + + if (!text || !lang) { + return response.sendStatus(400); + } + + console.log('Input text: ' + text); + + const url = generateRequestUrl(text, { to: lang }); + + https.get(url, (resp) => { + let data = ''; + + resp.on('data', (chunk) => { + data += chunk; + }); + + resp.on('end', () => { + const result = normaliseResponse(JSON.parse(data)); + console.log('Translated text: ' + result.text); + return response.send(result.text); + }); + }).on("error", (err) => { + console.log("Translation error: " + err.message); + return response.sendStatus(500); + }); + }); + + app.post('/api/translate/deepl', jsonParser, async (request, response) => { + const key = readSecret(SECRET_KEYS.DEEPL); + + if (!key) { + return response.sendStatus(401); + } + + const text = request.body.text; + const lang = request.body.lang; + + if (!text || !lang) { + return response.sendStatus(400); + } + + console.log('Input text: ' + text); + + const params = new URLSearchParams(); + params.append('text', text); + params.append('target_lang', lang); + + try { + const result = await fetch('https://api-free.deepl.com/v2/translate', { + method: 'POST', + body: params, + headers: { + 'Accept': 'application/json', + 'Authorization': `DeepL-Auth-Key ${key}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + timeout: 0, + }); + + if (!result.ok) { + const error = await result.text(); + console.log('DeepL error: ', result.statusText, error); + return response.sendStatus(result.status); + } + + const json = await result.json(); + console.log('Translated text: ' + json.translations[0].text); + + return response.send(json.translations[0].text); + } catch (error) { + console.log("Translation error: " + error.message); + return response.sendStatus(500); + } + }); + + app.post('/api/translate/onering', jsonParser, async (request, response) => { + const secretUrl = readSecret(SECRET_KEYS.ONERING_URL); + const url = secretUrl || ONERING_URL_DEFAULT; + + if (!url) { + console.log('OneRing URL is not configured.'); + return response.sendStatus(401); + } + + if (!secretUrl && url === ONERING_URL_DEFAULT) { + console.log('OneRing URL is using default value.', ONERING_URL_DEFAULT); + } + + const text = request.body.text; + const from_lang = request.body.from_lang; + const to_lang = request.body.to_lang; + + if (!text || !from_lang || !to_lang) { + return response.sendStatus(400); + } + + const params = new URLSearchParams(); + params.append('text', text); + params.append('from_lang', from_lang); + params.append('to_lang', to_lang); + + console.log('Input text: ' + text); + + try { + const fetchUrl = new URL(url); + fetchUrl.search = params.toString(); + + const result = await fetch(fetchUrl, { + method: 'GET', + timeout: 0, + }); + + if (!result.ok) { + const error = await result.text(); + console.log('OneRing error: ', result.statusText, error); + return response.sendStatus(result.status); + } + + const data = await result.json(); + console.log('Translated text: ' + data.result); + + return response.send(data.result); + } catch (error) { + console.log("Translation error: " + error.message); + return response.sendStatus(500); + } + }); + + app.post('/api/translate/deeplx', jsonParser, async (request, response) => { + const secretUrl = readSecret(SECRET_KEYS.DEEPLX_URL); + const url = secretUrl || DEEPLX_URL_DEFAULT; + + if (!url) { + console.log('DeepLX URL is not configured.'); + return response.sendStatus(401); + } + + if (!secretUrl && url === DEEPLX_URL_DEFAULT) { + console.log('DeepLX URL is using default value.', DEEPLX_URL_DEFAULT); + } + + const text = request.body.text; + const lang = request.body.lang; + + if (!text || !lang) { + return response.sendStatus(400); + } + + console.log('Input text: ' + text); + + try { + const result = await fetch(url, { + method: 'POST', + body: JSON.stringify({ + text: text, + source_lang: 'auto', + target_lang: lang, + }), + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + timeout: 0, + }); + + if (!result.ok) { + const error = await result.text(); + console.log('DeepLX error: ', result.statusText, error); + return response.sendStatus(result.status); + } + + const json = await result.json(); + console.log('Translated text: ' + json.data); + + return response.send(json.data); + } catch (error) { + console.log("DeepLX translation error: " + error.message); + return response.sendStatus(500); + } + }); +} + +module.exports = { + registerEndpoints, +}; diff --git a/src/util.js b/src/util.js new file mode 100644 index 000000000..a870eb169 --- /dev/null +++ b/src/util.js @@ -0,0 +1,10 @@ +const path = require('path'); + +function getConfig() { + const config = require(path.join(process.cwd(), './config.conf')); + return config; +} + +module.exports = { + getConfig, +}; diff --git a/src/vectors.js b/src/vectors.js new file mode 100644 index 000000000..d42a812f9 --- /dev/null +++ b/src/vectors.js @@ -0,0 +1,221 @@ +const express = require('express'); +const vectra = require('vectra'); +const path = require('path'); +const sanitize = require('sanitize-filename'); + +/** + * Gets the vector for the given text from the given source. + * @param {string} source - The source of the vector + * @param {string} text - The text to get the vector for + * @returns {Promise} - The vector for the text + */ +async function getVector(source, text) { + switch (source) { + case 'local': + return require('./local-vectors').getLocalVector(text); + case 'openai': + return require('./openai-vectors').getOpenAIVector(text); + } + + throw new Error(`Unknown vector source ${source}`); +} + +/** + * Gets the index for the vector collection + * @param {string} collectionId - The collection ID + * @param {string} source - The source of the vector + * @param {boolean} create - Whether to create the index if it doesn't exist + * @returns {Promise} - The index for the collection + */ +async function getIndex(collectionId, source, create = true) { + const index = new vectra.LocalIndex(path.join(process.cwd(), 'vectors', sanitize(source), sanitize(collectionId))); + + if (create && !await index.isIndexCreated()) { + await index.createIndex(); + } + + return index; +} + +/** + * Inserts items into the vector collection + * @param {string} collectionId - The collection ID + * @param {string} source - The source of the vector + * @param {{ hash: number; text: string; }[]} items - The items to insert + */ +async function insertVectorItems(collectionId, source, items) { + const index = await getIndex(collectionId, source); + + await index.beginUpdate(); + + for (const item of items) { + const text = item.text; + const hash = item.hash; + const vector = await getVector(source, text); + await index.upsertItem({ vector: vector, metadata: { hash, text } }); + } + + await index.endUpdate(); +} + +/** + * Gets the hashes of the items in the vector collection + * @param {string} collectionId - The collection ID + * @param {string} source - The source of the vector + * @returns {Promise} - The hashes of the items in the collection + */ +async function getSavedHashes(collectionId, source) { + const index = await getIndex(collectionId, source); + + const items = await index.listItems(); + const hashes = items.map(x => Number(x.metadata.hash)); + + return hashes; +} + +/** + * Deletes items from the vector collection by hash + * @param {string} collectionId - The collection ID + * @param {string} source - The source of the vector + * @param {number[]} hashes - The hashes of the items to delete + */ +async function deleteVectorItems(collectionId, source, hashes) { + const index = await getIndex(collectionId, source); + const items = await index.listItemsByMetadata({ hash: { '$in': hashes } }); + + await index.beginUpdate(); + + for (const item of items) { + await index.deleteItem(item.id); + } + + await index.endUpdate(); +} + +/** + * Gets the hashes of the items in the vector collection that match the search text + * @param {string} collectionId - The collection ID + * @param {string} source - The source of the vector + * @param {string} searchText - The text to search for + * @param {number} topK - The number of results to return + * @returns {Promise} - The hashes of the items that match the search text + */ +async function queryCollection(collectionId, source, searchText, topK) { + const index = await getIndex(collectionId, source); + const vector = await getVector(source, searchText); + + const result = await index.queryItems(vector, topK); + const hashes = result.map(x => Number(x.item.metadata.hash)); + return hashes; +} + +/** + * Registers the endpoints for the vector API + * @param {express.Express} app - Express app + * @param {any} jsonParser - Express JSON parser + */ +async function registerEndpoints(app, jsonParser) { + app.post('/api/vector/query', jsonParser, async (req, res) => { + try { + if (!req.body.collectionId || !req.body.searchText) { + return res.sendStatus(400); + } + + const collectionId = String(req.body.collectionId); + const searchText = String(req.body.searchText); + const topK = Number(req.body.topK) || 10; + const source = String(req.body.source) || 'local'; + + const results = await queryCollection(collectionId, source, searchText, topK); + return res.json(results); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } + }); + + app.post('/api/vector/insert', jsonParser, async (req, res) => { + try { + if (!Array.isArray(req.body.items) || !req.body.collectionId) { + return res.sendStatus(400); + } + + const collectionId = String(req.body.collectionId); + const items = req.body.items.map(x => ({ hash: x.hash, text: x.text })); + const source = String(req.body.source) || 'local'; + + await insertVectorItems(collectionId, source, items); + return res.sendStatus(200); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } + }); + + app.post('/api/vector/list', jsonParser, async (req, res) => { + try { + if (!req.body.collectionId) { + return res.sendStatus(400); + } + + const collectionId = String(req.body.collectionId); + const source = String(req.body.source) || 'local'; + + const hashes = await getSavedHashes(collectionId, source); + return res.json(hashes); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } + }); + + app.post('/api/vector/delete', jsonParser, async (req, res) => { + try { + if (!Array.isArray(req.body.hashes) || !req.body.collectionId) { + return res.sendStatus(400); + } + + const collectionId = String(req.body.collectionId); + const hashes = req.body.hashes.map(x => Number(x)); + const source = String(req.body.source) || 'local'; + + await deleteVectorItems(collectionId, source, hashes); + return res.sendStatus(200); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } + }); + + app.post('/api/vector/purge', jsonParser, async (req, res) => { + try { + if (!req.body.collectionId) { + return res.sendStatus(400); + } + + const collectionId = String(req.body.collectionId); + + const sources = ['local', 'openai']; + for (const source of sources) { + const index = await getIndex(collectionId, source, false); + + const exists = await index.isIndexCreated(); + + if (!exists) { + continue; + } + + const path = index.folderPath; + await index.deleteIndex(); + console.log(`Deleted vector index at ${path}`); + } + + return res.sendStatus(200); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } + }); +} + +module.exports = { registerEndpoints };