From 11e0049dae8238ec36fdbe6e0a6941920134f22b Mon Sep 17 00:00:00 2001 From: svmlitimur Date: Fri, 10 May 2024 13:17:20 +0300 Subject: [PATCH 1/5] Converted to module and optimise PR-URL: https://github.com/Universal-Code-Modules/universal-llm/pull/1 --- .cert/cert.pem | 26 - .cert/key.pem | 28 - .editorconfig | 12 + .eslintignore | 130 + .eslintrc.json | 161 + .gitattributes | 1 + .github/workflows/test.yml | 36 + .gitignore | 7 +- .npmignore | 8 + .prettierignore | 2 + .prettierrc | 4 + Elevenlabs/elevenlabs-connector.js | 126 - Elevenlabs/tests/elevenlabs-connector.test.js | 104 - Huggingface/huggingface-connector.js | 375 - .../tests/huggingface-connector.test.js | 418 -- Ollama/ollama-connector.js | 42 - Ollama/tests/ollama-connector.test.js | 22 - OpenAI/openai-config.js | 15 - OpenAI/openai-connector.js | 972 --- OpenAI/openai-data.js | 126 - OpenAI/openai-tokens-price.js | 194 - OpenAI/tests/openai-connector.test.js | 369 - OpenAI/tests/speech/test-speech-output-en.mp3 | Bin 38880 -> 0 bytes OpenAI/tests/tools/test-library.js | 55 - README.md | 152 +- config.js | 8 - config.json | 12 + .../js/main.js => files/elevenlabs/.gitkeep | 0 files/elevenlabs/audio.mp3 | Bin 0 -> 10866 bytes .../huggingface/audios/speech.mp3 | Bin .../huggingface/images/cat.jpg | Bin .../huggingface/images/invoice.png | Bin .../huggingface/images/see.jpeg | Bin files/ollama/.gitkeep | 0 .../openai}/assistants/test.csv | 0 .../openai}/assistants/test.numbers | Bin .../openai/audios}/test-speech-input-de.mp3 | Bin .../openai/audios}/test-speech-input-en.mp3 | Bin .../openai/audios}/test-speech-input-he.mp3 | Bin .../openai/audios}/test-speech-input-ru.mp3 | Bin .../openai/audios}/test-speech-output-de.mp3 | Bin files/openai/audios/test-speech-output-en.mp3 | Bin 0 -> 42240 bytes .../openai/audios}/test-speech-output-he.mp3 | Bin .../openai/audios}/test-speech-output-ru.mp3 | Bin .../openai/audios}/test-speech-output.mp3 | Bin .../openai}/fine-tune/test-fine-tune-24.jsonl | 0 .../openai}/images/test-edit-image-result.jpg | Bin .../openai}/images/test-edit-image.png | Bin .../images/test-image-create-result.jpg | Bin .../openai}/images/test-image-result.jpg | Bin .../images/test-image-variation-result.jpg | Bin .../openai}/images/test-image.jpg | Bin .../openai}/images/test-image.png | Bin files/openai/tools/test-library.js | 95 + .../tests => files/openai}/videos/cat-no.mp4 | Bin jest.config.js | 200 - lib/common.js | 230 + lib/elevenlabs/connector.js | 142 + lib/huggingface/connector.js | 478 ++ lib/index.js | 8 + lib/ollama/connector.js | 36 + lib/openai/config.json | 14 + lib/openai/connector.js | 154 + lib/openai/data.js | 253 + lib/openai/utils/assistants.js | 437 ++ lib/openai/utils/files.js | 64 + lib/openai/utils/finetune.js | 112 + lib/openai/utils/images.js | 114 + lib/openai/utils/index.js | 15 + lib/openai/utils/language.js | 167 + lib/openai/utils/models.js | 39 + lib/openai/utils/price.js | 241 + lib/openai/utils/recognation.js | 112 + lib/openai/utils/speech.js | 79 + lib/openai/utils/tokens.js | 27 + lib/openai/utils/tools.js | 40 + package-lock.json | 6045 ++++------------- package.json | 67 +- public/index.html | 58 - server.js | 44 - test/elevenlabs.js | 109 + test/huggingface.js | 439 ++ test/ollama.js | 23 + test/openai.js | 403 ++ tsconfig.json | 11 + utilities.js | 204 - 86 files changed, 5501 insertions(+), 8364 deletions(-) delete mode 100644 .cert/cert.pem delete mode 100644 .cert/key.pem create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .gitattributes create mode 100644 .github/workflows/test.yml create mode 100644 .npmignore create mode 100644 .prettierignore create mode 100644 .prettierrc delete mode 100644 Elevenlabs/elevenlabs-connector.js delete mode 100644 Elevenlabs/tests/elevenlabs-connector.test.js delete mode 100644 Huggingface/huggingface-connector.js delete mode 100644 Huggingface/tests/huggingface-connector.test.js delete mode 100644 Ollama/ollama-connector.js delete mode 100644 Ollama/tests/ollama-connector.test.js delete mode 100644 OpenAI/openai-config.js delete mode 100644 OpenAI/openai-connector.js delete mode 100644 OpenAI/openai-data.js delete mode 100644 OpenAI/openai-tokens-price.js delete mode 100644 OpenAI/tests/openai-connector.test.js delete mode 100644 OpenAI/tests/speech/test-speech-output-en.mp3 delete mode 100644 OpenAI/tests/tools/test-library.js delete mode 100644 config.js create mode 100644 config.json rename public/js/main.js => files/elevenlabs/.gitkeep (100%) create mode 100644 files/elevenlabs/audio.mp3 rename Huggingface/tests/test-speech.mp3 => files/huggingface/audios/speech.mp3 (100%) rename Huggingface/tests/test-cat.jpg => files/huggingface/images/cat.jpg (100%) rename Huggingface/tests/test-invoice.png => files/huggingface/images/invoice.png (100%) rename Huggingface/tests/test-see.jpeg => files/huggingface/images/see.jpeg (100%) create mode 100644 files/ollama/.gitkeep rename {OpenAI/tests => files/openai}/assistants/test.csv (100%) rename {OpenAI/tests => files/openai}/assistants/test.numbers (100%) rename {OpenAI/tests/speech => files/openai/audios}/test-speech-input-de.mp3 (100%) rename {OpenAI/tests/speech => files/openai/audios}/test-speech-input-en.mp3 (100%) rename {OpenAI/tests/speech => files/openai/audios}/test-speech-input-he.mp3 (100%) rename {OpenAI/tests/speech => files/openai/audios}/test-speech-input-ru.mp3 (100%) rename {OpenAI/tests/speech => files/openai/audios}/test-speech-output-de.mp3 (100%) create mode 100644 files/openai/audios/test-speech-output-en.mp3 rename {OpenAI/tests/speech => files/openai/audios}/test-speech-output-he.mp3 (100%) rename {OpenAI/tests/speech => files/openai/audios}/test-speech-output-ru.mp3 (100%) rename {OpenAI/tests/speech => files/openai/audios}/test-speech-output.mp3 (100%) rename {OpenAI/tests => files/openai}/fine-tune/test-fine-tune-24.jsonl (100%) rename {OpenAI/tests => files/openai}/images/test-edit-image-result.jpg (100%) rename {OpenAI/tests => files/openai}/images/test-edit-image.png (100%) rename {OpenAI/tests => files/openai}/images/test-image-create-result.jpg (100%) rename {OpenAI/tests => files/openai}/images/test-image-result.jpg (100%) rename {OpenAI/tests => files/openai}/images/test-image-variation-result.jpg (100%) rename {OpenAI/tests => files/openai}/images/test-image.jpg (100%) rename {OpenAI/tests => files/openai}/images/test-image.png (100%) create mode 100644 files/openai/tools/test-library.js rename {OpenAI/tests => files/openai}/videos/cat-no.mp4 (100%) delete mode 100644 jest.config.js create mode 100644 lib/common.js create mode 100644 lib/elevenlabs/connector.js create mode 100644 lib/huggingface/connector.js create mode 100644 lib/index.js create mode 100644 lib/ollama/connector.js create mode 100644 lib/openai/config.json create mode 100644 lib/openai/connector.js create mode 100644 lib/openai/data.js create mode 100644 lib/openai/utils/assistants.js create mode 100644 lib/openai/utils/files.js create mode 100644 lib/openai/utils/finetune.js create mode 100644 lib/openai/utils/images.js create mode 100644 lib/openai/utils/index.js create mode 100644 lib/openai/utils/language.js create mode 100644 lib/openai/utils/models.js create mode 100644 lib/openai/utils/price.js create mode 100644 lib/openai/utils/recognation.js create mode 100644 lib/openai/utils/speech.js create mode 100644 lib/openai/utils/tokens.js create mode 100644 lib/openai/utils/tools.js delete mode 100644 public/index.html delete mode 100644 server.js create mode 100644 test/elevenlabs.js create mode 100644 test/huggingface.js create mode 100644 test/ollama.js create mode 100644 test/openai.js create mode 100644 tsconfig.json delete mode 100644 utilities.js diff --git a/.cert/cert.pem b/.cert/cert.pem deleted file mode 100644 index 3c068bb..0000000 --- a/.cert/cert.pem +++ /dev/null @@ -1,26 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIETTCCArWgAwIBAgIRAPLCz/oMzHW0xFFIm8UFzc4wDQYJKoZIhvcNAQELBQAw -gYkxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEvMC0GA1UECwwmbGVv -bmlkcG9sYWtATGVvbmlkcy1NQlAgKExlb25pZCBQb2xhaykxNjA0BgNVBAMMLW1r -Y2VydCBsZW9uaWRwb2xha0BMZW9uaWRzLU1CUCAoTGVvbmlkIFBvbGFrKTAeFw0y -NDAyMjYxMjAwMzNaFw0yNjA1MjYxMTAwMzNaMFoxJzAlBgNVBAoTHm1rY2VydCBk -ZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTEvMC0GA1UECwwmbGVvbmlkcG9sYWtATGVv -bmlkcy1NQlAgKExlb25pZCBQb2xhaykwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCfh2a7qaO4sfLfVxjONMlYZIh091U8lVU/zZ0zN5Q9GYYxeWMNK5DA -FR+JouDyz3C5rYZSfR+E4aLPV1mIuZaeyy/8hAtytlJX+LzonGo23XfyI8Q8OzV5 -NIQHpLXnX1RneXSLnF1I6z2nb3VtJHzB5oJW3htEQxdF6Egh669e/IElwjvIvBB1 -Dlusk60YaLmWYyfCImCku2C/IgiYObgnLtuytDE+6EOvWSu4fnWZVkJNA3p1jUHg -fOnrJhOpTHBUIm++i9QySa0uh/7Qqa3k14+kjiIdXwe7/f8PXnDqYJ2sLnEFcUxh -u/54cOA0fzYOhcUWfA0qy6/lnHxbI/B3AgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIF -oDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBR9C6a/Wy2ShTLIAecR -aZdApabOWTAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGB -AEbHy5litC9SiqFHu6GtlQV/TM2F/I6RTCeK+8WsvqMABR29WzinkF5NeDj2+PFq -S/bnlDeare/Wua66Pi8ySrexquba13WJREt4+sJ4a2tas7uSUAnQE1y8+lL8eQJc -xzPtIc/ihM4N0trnhQCJdaYo1sBv5ylFacIIavJseoqH6oS/RoVked78IH/pKBSF -GkbhY541Rz7WIIsfgBMWnD2TRz5tsGoURzOasapwuKEICe9uqKep+7SFmJg8O8uo -A2R9VjL7/dZrwwICYZH/UxkOE+0ogB4lW1ZJfFJF57cBRMb6m+PAW2PVzVM/Lu34 -yoFtCYdl9gGztLsjqIP1plKYMfQAJ7WH/v6MFD7pQUYFIF5r4AAlWswdH8XGDdP/ -uZMfrHhAgHE4VQOfnl7dzL1ZvWcV3a7essUl5wLXhPeXUr/1dm6UbMM0fGWGAH/y -Oxys+ZZSGWZ7D3aIT9i+ldWBLztKT45MQwJoO86pvi3SpG7RqauJJ8VTb941wv/D -ZA== ------END CERTIFICATE----- diff --git a/.cert/key.pem b/.cert/key.pem deleted file mode 100644 index 841a2bf..0000000 --- a/.cert/key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCfh2a7qaO4sfLf -VxjONMlYZIh091U8lVU/zZ0zN5Q9GYYxeWMNK5DAFR+JouDyz3C5rYZSfR+E4aLP -V1mIuZaeyy/8hAtytlJX+LzonGo23XfyI8Q8OzV5NIQHpLXnX1RneXSLnF1I6z2n -b3VtJHzB5oJW3htEQxdF6Egh669e/IElwjvIvBB1Dlusk60YaLmWYyfCImCku2C/ -IgiYObgnLtuytDE+6EOvWSu4fnWZVkJNA3p1jUHgfOnrJhOpTHBUIm++i9QySa0u -h/7Qqa3k14+kjiIdXwe7/f8PXnDqYJ2sLnEFcUxhu/54cOA0fzYOhcUWfA0qy6/l -nHxbI/B3AgMBAAECggEAZCqiiObFVCz3sOCBRMjEO+oz/C1oqbeiZYN3Vzdorsye -A+aSGSznoJQiz0skL5Gs2Alqs9S4u7jpf4L7Ruww9Nfj84v9nDN6lJ4sD2pXDsgU -jkeC6cbLlqUw2ZPEW9jxqF8MnLS7phN/DOjhNHyakS1pXZ3sRPpTXKOy1wmjfeHU -RcLhh4GwXw730I+ML6XfWoiuNk/2gJzUwTnQ7iWv4vB+0C+7NvhPS0tboq21yOKB -LKOEjjN+EO/itQVAzXyWuGeY8NuiAMkzYm95/f/mKlvXtgscd3EXPoQbXBWyZodX -fTvUsGCN1EZaDL+1dQQIR1/DHUSL1uS2TPtWoaUC0QKBgQDCeo0WqI6MXRUkVlrc -cx8x+z6986j8NhFPx8Ww9Sl8IRTZCuPqyEm8GvAZM26b5hQk+9tWOZpgPJI74c57 -JqQIfaelG6nj/538CuboHruxxL/vaIR5bT0F4EXA+ZBCzpdzZHyJegPjgjm0SM45 -uyGcGeykxuNtWzYuIYR/m8oZrQKBgQDR/oOSujSom2r7pEOPfYvUEuL2VgHEYjOI -VlERkx76dieJY4bk9hS6y20ZDT8tjrnVdKmrtR+1lFoKXXPHU3sAn3U4sqfjwMdp -jGzdoCNJXY6fnPVAG3AXPI/hafIsMObm8FeGMeW82nTM4qs784CUvu8vu6J4vt7f -IoIMODR/MwKBgQDBTz2k0yD2ZqPYPboNPKU6ckmYWHWVVUz551IbJmX5C40EfK22 -NyYLwx77fcMpVZaaeHo6CAHqPOsCeN5cTTlpczVLiebiSbhIyrFg8wOQnUhHebWM -WUf56kaLncWGkoG6YpzjrhPldhFTobbTba3n02EFHOj9T1fYnNpuWXWW9QKBgEaw -I0BIGCPNtXSh9fAedexcszPy/cNKv0gcrkt5N8td7K8t5qG6SH4crkvpjqPaUKoP -DHJ1lQf9uVWV4bxuWJB8HOyKD0P2h+n60EbGejpfGPyXzGLZGaFozJI3pMLFb2S7 -8OqGb2xkKT8y6itTK6xmnYN26Rk2PzXjmt1cKifLAoGBALVFuhhFgHxiApyEWOQy -5cQ9dpYWm5frpawXlE1RopkT4LLqE3cVnuiDE8zQELJSjYvs4f2VB+6SEMFETITz -xyHNQZ3JN+FTmUiT2ntmOE8/AFUKZU903jJXsRcIuWzWZEiBeWRxziIZUqifHor/ -I6MQd2L/9s/ToI/Kci6TqYYw ------END PRIVATE KEY----- diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dde7877 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# http://editorconfig.org +root = true + +[*] +end_of_line = lf +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +[{*.js,*.mjs,*.ts,*.json,*.yml}] +indent_size = 2 +indent_style = space diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..c6bba59 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,130 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..82e5d85 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,161 @@ +{ + "env": { + "browser": true, + "es6": true, + "node": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": "latest" + }, + "globals": { + "BigInt": true + }, + "rules": { + "no-empty": ["error", { "allowEmptyCatch": true }], + "indent": ["error", 2], + "linebreak-style": ["error", "unix"], + "quotes": ["error", "single"], + "semi": ["error", "always"], + "no-loop-func": ["error"], + "block-spacing": ["error", "always"], + "camelcase": ["off"], + "eqeqeq": ["error", "always"], + "strict": ["error", "global"], + "brace-style": [ + "error", + "1tbs", + { + "allowSingleLine": true + } + ], + "comma-style": ["error", "last"], + "comma-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "eol-last": ["error"], + "func-call-spacing": ["error", "never"], + "key-spacing": [ + "error", + { + "beforeColon": false, + "afterColon": true, + "mode": "minimum" + } + ], + "keyword-spacing": [ + "error", + { + "before": true, + "after": true, + "overrides": { + "function": { + "after": false + } + } + } + ], + "max-len": [ + "error", + { + "code": 80, + "ignoreUrls": true + } + ], + "max-nested-callbacks": [ + "error", + { + "max": 7 + } + ], + "new-cap": [ + "error", + { + "newIsCap": true, + "capIsNew": false, + "properties": true + } + ], + "new-parens": ["error"], + "no-lonely-if": ["error"], + "no-trailing-spaces": ["error"], + "no-unneeded-ternary": ["error"], + "no-whitespace-before-property": ["error"], + "object-curly-spacing": ["error", "always"], + "operator-assignment": ["error", "always"], + "operator-linebreak": ["error", "after"], + "semi-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "space-before-blocks": ["error", "always"], + "space-before-function-paren": [ + "error", + { + "anonymous": "never", + "named": "never", + "asyncArrow": "always" + } + ], + "space-in-parens": ["error", "never"], + "space-infix-ops": ["error"], + "space-unary-ops": [ + "error", + { + "words": true, + "nonwords": false, + "overrides": { + "typeof": false + } + } + ], + "no-unreachable": ["error"], + "no-global-assign": ["error"], + "no-self-compare": ["error"], + "no-unmodified-loop-condition": ["error"], + "no-constant-condition": [ + "error", + { + "checkLoops": false + } + ], + "no-console": ["off"], + "no-useless-concat": ["error"], + "no-useless-escape": ["error"], + "no-shadow-restricted-names": ["error"], + "no-use-before-define": [ + "error", + { + "functions": false + } + ], + "arrow-parens": ["error", "always"], + "arrow-body-style": ["error", "as-needed"], + "arrow-spacing": ["error"], + "no-confusing-arrow": [ + "error", + { + "allowParens": true + } + ], + "no-useless-computed-key": ["error"], + "no-useless-rename": ["error"], + "no-var": ["error"], + "object-shorthand": ["error", "always"], + "prefer-arrow-callback": ["error"], + "prefer-const": ["error"], + "prefer-numeric-literals": ["error"], + "prefer-rest-params": ["error"], + "prefer-spread": ["error"], + "rest-spread-spacing": ["error", "never"], + "template-curly-spacing": ["error", "never"], + "consistent-return": ["error", { "treatUndefinedAsUnspecified": true }] + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fa1385d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* -text diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..9302e5a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,36 @@ +name: Testing CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + build: + if: false + runs-on: ${{ matrix.os }} + strategy: + matrix: + node: + - 18 + - 20 + - 21 + os: + - ubuntu-latest + - windows-latest + - macos-latest + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - run: npm ci + - run: npm test diff --git a/.gitignore b/.gitignore index 322d7b9..2429465 100644 --- a/.gitignore +++ b/.gitignore @@ -131,13 +131,14 @@ dist -#index.js is for local testing only -index.js +#main.js is for local testing only +main.js #mac .DS_Store +.DS_Store? #store temp files for testing temp #keep private testing files my_* #don't include temporary video frames -video-frames \ No newline at end of file +video-frames diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..3bfe3c6 --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +node_modules/ +.npm +.eslintcache +.yarn-integrity +.env +.env.test +*.log +*.tgz diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..cce0279 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +package.json +package-lock.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a20502b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/Elevenlabs/elevenlabs-connector.js b/Elevenlabs/elevenlabs-connector.js deleted file mode 100644 index 96663e0..0000000 --- a/Elevenlabs/elevenlabs-connector.js +++ /dev/null @@ -1,126 +0,0 @@ -require('dotenv').config(); -const ut = require(process.cwd() + '/utilities'); - -const ELEVENLABS_API_KEY = process.env.ELEVENLABS_API_KEY; -const ElevenLabs = require("elevenlabs-node"); - -const voice = new ElevenLabs( - { - apiKey: ELEVENLABS_API_KEY, // Your API key from Elevenlabs - // voiceId: "pNInz6obpgDQGcFmaJgB", // A Voice ID from Elevenlabs (Adam) - } -); - - - - - -/* - Required Parameters - fileName: "audio.mp3", // The name of your audio file - textInput: "mozzy is cool", // The text you wish to convert to speech - - Optional Parameters - voiceId: "21m00Tcm4TlvDq8ikWAM", // A different Voice ID from the default - stability: 0.5, // The stability for the converted speech - similarityBoost: 0.5, // The similarity boost for the converted speech - modelId: "eleven_multilingual_v2", // The ElevenLabs Model ID - style: 1, // The style exaggeration for the converted speech - speakerBoost: true // The speaker boost for the converted speech - - - fileName Name and file path for your audio file e.g (./gen/hello) String - textInput Text to be converted into audio e.g (Hello) String - stability Stability for Text to Speech default (0) Float - similarityBoost Similarity Boost for Text to Speech default (0) Float - voiceId ElevenLabs Voice ID e.g (pNInz6obpgDQGcFmaJgB) String - modelId ElevenLabs Model ID e.g (eleven_multilingual_v2) String - responseType Streaming response type e.g (stream) String - speakerBoost Speaker Boost for Text to Speech e.g (true) Boolean - style Style Exaggeration for Text to Speech (0-100) default (0) Integer - -*/ - - - const textToSpeech = async( - textInput = 'Hello World', - fileName = "./tests/test-audio.mp3", - voiceId = "pNInz6obpgDQGcFmaJgB" - ) => { - - const result = await ut.callAPI(voice, 'voice.textToSpeech', { textInput, fileName, voiceId }); - return result; - } -//{voiceId, textInput, stability, similarityBoost, modelId, responseType, style, speakerBoost} -const textToSpeechStream = async ( - voiceId, textInput, stability, similarityBoost, modelId, responseType, style, speakerBoost -) => { - const result = await ut.callAPI(voice, 'voice.textToSpeechStream', { voiceId, textInput, stability, similarityBoost, modelId, responseType, style, speakerBoost }); - -} - - const getVoices = async() => { - const result = await ut.callAPI(voice, 'voice.getVoices') ; - return result; - } - - const getVoice = async(voiceId = "pNInz6obpgDQGcFmaJgB") => { - - const result = await ut.callAPI(voice, 'voice.getVoice', {voiceId}); - return result; - } - -const editVoiceSettings = async ( voiceId, stability, similarityBoost ) => { - - const result = await ut.callAPI(voice, 'voice.editVoiceSettings', { voiceId, stability, similarityBoost }); - return result; -} -const getVoiceSettings = async ( voiceId ) => { - const result = await ut.callAPI(voice, 'voice.getVoiceSettings', { voiceId }); - return result; - } -const deleteVoice = async (voiceId ) => { - const result = await ut.callAPI(voice, 'voice.deleteVoice', { voiceId }); - return result; -} - -const getModels = async () => { - const result = await ut.callAPI(voice, 'voice.getModels'); - return result; -} -const getUserInfo = async () => { - const result = await ut.callAPI(voice, 'voice.getUserInfo'); - return result; -} -const getUserSubscription = async () => { - const result = await ut.callAPI(voice, 'voice.getUserSubscription'); - return result; -} -const getDefaultVoiceSettings = async () => { - const result = await ut.callAPI(voice, 'voice.getDefaultVoiceSettings'); - return result; -} - - - - - // async deleteVoice(voiceId = "pNInz6obpgDQGcFmaJgB") { - // const result = await voice.deleteVoice({ voiceId }); - // if (TEST) console.log(result); - // return result; - // } - - -module.exports = { - textToSpeech, - textToSpeechStream, - getVoices, - getVoice, - editVoiceSettings, - getVoiceSettings, - deleteVoice, - getModels, - getUserInfo, - getUserSubscription, - getDefaultVoiceSettings -}; \ No newline at end of file diff --git a/Elevenlabs/tests/elevenlabs-connector.test.js b/Elevenlabs/tests/elevenlabs-connector.test.js deleted file mode 100644 index b7d5923..0000000 --- a/Elevenlabs/tests/elevenlabs-connector.test.js +++ /dev/null @@ -1,104 +0,0 @@ -const { - textToSpeech, - textToSpeechStream, - getVoices, - getVoice, - editVoiceSettings, - getVoiceSettings, - deleteVoice, - getModels, - getUserInfo, - getUserSubscription, - getDefaultVoiceSettings -} = require('../elevenlabs-connector'); - - -jest.setTimeout(20000); - -describe('Elevenlabs Connector', () => { - - - test("textToSpeech", async () => { - const res = await textToSpeech(''); - - // console.log(res) - expect(res).toBeArray(); - - - }); - test("textToSpeechStream", async () => { - const res = await textToSpeechStream(''); - - // console.log(res) - expect(res).toBeArray(); - - - }); - - test("getVoices", async () => { - const res = await getVoices(); - - // console.log(res) - expect(res).toBeArray(); - - - }); - - test("getVoice", async () => { - const res = await getVoice(); - - // console.log(res) - expect(res).toBeArray(); - }); - - test("editVoiceSettings", async () => { - const res = await editVoiceSettings(); - - // console.log(res) - expect(res).toBeArray(); - }); - - test("getVoiceSettings", async () => { - const res = await getVoiceSettings(); - - // console.log(res) - expect(res).toBeArray(); - }); - - - test("deleteVoice", async () => { - const res = await deleteVoice(); - - // console.log(res) - expect(res).toBeArray(); - }); - - test("getModels", async () => { - const res = await getModels(); - - // console.log(res) - expect(res).toBeArray(); - }); - test("getUserInfo", async () => { - const res = await getUserInfo(); - - // console.log(res) - expect(res).toBeArray(); - }); - test("getUserSubscription", async () => { - const res = await getUserSubscription(); - - // console.log(res) - expect(res).toBeArray(); - }); - test("getDefaultVoiceSettings", async () => { - const res = await getDefaultVoiceSettings(); - - // console.log(res) - expect(res).toBeArray(); - }); - - - - -}) \ No newline at end of file diff --git a/Huggingface/huggingface-connector.js b/Huggingface/huggingface-connector.js deleted file mode 100644 index 300aa42..0000000 --- a/Huggingface/huggingface-connector.js +++ /dev/null @@ -1,375 +0,0 @@ -require('dotenv').config(); -const{ HfInference } = require("@huggingface/inference"); - -const ut = require(process.cwd() + '/utilities'); - -const HUGGINGFACE_TOKEN = process.env.HUGGINGFACE_TOKEN; - -const hf = new HfInference(HUGGINGFACE_TOKEN); - -// You can also omit "model" to use the recommended model for the task - - // constructor() { - - // } - - - - //......Natural Language Processing - /* - inputs = '[MASK] world!' - */ - const FillMask = async (inputs, model = 'bert-base-uncased') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.fillMask', args); - return res; - } - /* - inputs = `The tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey building, and the tallest structure in Paris. - Its base is square, measuring 125 metres (410 ft) on each side. During its construction, the Eiffel Tower surpassed the Washington Monument to become the tallest`, - model = 'facebook/bart-large-cnn' - */ - const Summarization = async (inputs, parameters = {max_length: 100}, model = 'facebook/bart-large-cnn') => { - const args = {inputs, parameters, model}; - const res = await ut.callAPI(hf , 'hf.summarization', args); - return res; - } - /* - inputs = { - question: 'What is the capital of France?', - context: 'The capital of France is Paris.' - }, - */ - const QuestionAnswering = async(inputs, model = 'deepset/roberta-base-squad2') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.questionAnswering', args); - return res; - } - /* - inputs = { - query: 'How many stars does the transformers repository have?', - table: { - Repository: ['Transformers', 'Datasets', 'Tokenizers'], - Stars: ['36542', '4512', '3934'], - Contributors: ['651', '77', '34'], - 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'] - } - }, - */ - const TableQuestionAnswering = async(inputs, model = 'google/tapas-base-finetuned-wtq') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.tableQuestionAnswering', args); - return res; - } - /* - inputs = 'I like you. I love you.' - */ - const TextClassification = async(inputs, model = 'distilbert-base-uncased-finetuned-sst-2-english') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.textClassification', args); - return res; - } - /* - inputs = 'The answer to the universe is' - */ - const TextGeneration= async(inputs, model = 'gpt2') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.textGeneration', args); - return res; - } - /* - inputs = 'repeat "one two three four"' - parameters = { max_new_tokens: 250 } - */ - const TextGenerationStream = async(inputs, parameters = {}, model = 'google/flan-t5-xxl') => { - const args = {inputs, parameters, model}; - const res = await ut.callAPI(hf , 'hf.textGenerationStream', args); - return res; - } - /* - inputs = 'My name is Sarah Jessica Parker but you can call me Jessica' - */ - const TokenClassification = async(inputs, model = 'dbmdz/bert-large-cased-finetuned-conll03-english') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.tokenClassification', args); - return res; - } - /* - inputs = 'My name is Wolfgang and I live in Amsterdam', - parameters = {"src_lang": "en_XX", "tgt_lang": "fr_XX"} - */ - const Translation = async(inputs, parameters = {}, model = 't5-base') => { - const args = {inputs, parameters, model}; - const res = await ut.callAPI(hf , 'hf.translation', args); - return res; - } - /* - inputs = [ - 'Hi, I recently bought a device from your company but it is not working as advertised and I would like to get reimbursed!' - ], - parameters = { candidate_labels: ['refund', 'legal', 'faq'] } - */ - const ZeroShotClassification = async(inputs, parameters = {}, model = 'facebook/bart-large-mnli') => { - const args = {inputs, parameters, model}; - const res = await ut.callAPI(hf , 'hf.zeroShotClassification', args); - return res; - } - /* - inputs = { - source_sentence: 'That is a happy person', - sentences: [ - 'That is a happy dog', - 'That is a very happy person', - 'Today is a sunny day' - ] - } - */ - const SentenceSimilarity = async(inputs, model = 'sentence-transformers/paraphrase-xlm-r-multilingual-v1') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.sentenceSimilarity', args); - return res; - } - - //.........Audio - /* - data = readFileSync('test/sample1.flac') - */ - const AutomaticSpeechRecognition = async (data, model = 'facebook/wav2vec2-large-960h-lv60-self') => { - const args = {data, model}; - const res = await ut.callAPI(hf , 'hf.automaticSpeechRecognition', args); - return res; - } - /* - data = readFileSync('test/sample1.flac') - */ - const AudioClassification = async(data, model = 'superb/hubert-large-superb-er') => { - const args = {data, model}; - const res = await ut.callAPI(hf , 'hf.audioClassification', args); - return res; - } - /* - inputs = 'Hello world!' - */ - const TextToSpeech = async(inputs, model = 'espnet/kan-bayashi_ljspeech_vits') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.textToSpeech', args); - return res; - } - /* - data = readFileSync('test/sample1.flac') - */ - const AudioToAudio = async(data, model = 'speechbrain/sepformer-wham') => { - const args = {data, model}; - const res = await ut.callAPI(hf , 'hf.audioToAudio', args); - return res; - } - - //........Computer Vision - /* - data = readFileSync('test/cheetah.png') - */ - const ImageClassification = async(data, model = 'google/vit-base-patch16-224') => { - const args = {data, model}; - const res = await ut.callAPI(hf , 'hf.imageClassification', args); - return res; - } - /* - data = readFileSync('test/cats.png') - */ - const ObjectDetection = async(data, model = 'facebook/detr-resnet-50') => { - const args = {data, model}; - const res = await ut.callAPI(hf , 'hf.objectDetection', args); - return res; - } - /* - data = readFileSync('test/cats.png') - */ - const ImageSegmentation = async(data, model = 'facebook/detr-resnet-50-panoptic') => { - const args = {data, model}; - const res = await ut.callAPI(hf , 'hf.imageSegmentation', args); - return res; - } - /* - data = await (await fetch('https://picsum.photos/300/300')).blob() - */ - const ImageToText = async(data, model = 'nlpconnect/vit-gpt2-image-captioning') => { - const args = {data, model}; - const res = await ut.callAPI(hf , 'hf.imageToText', args); - return res; - } - /* - inputs = 'award winning high resolution photo of a giant tortoise/((ladybird)) hybrid, [trending on artstation]', - parameters = {negative_prompt: 'blurry'}, - */ - const TextToImage = async(inputs, - parameters = {}, - model = 'stabilityai/stable-diffusion-2') => { - const args = {inputs, parameters, model}; - const res = await ut.callAPI(hf , 'hf.textToImage', args); - return res; - } - /* - inputs = new Blob([readFileSync("test/stormtrooper_depth.png")]), - parameters = {prompt: "elmo's lecture"}, - */ - const ImageToImage = async(inputs, parameters = {}, model = 'lllyasviel/sd-controlnet-depth') => { - const args = {inputs, parameters, model}; - const res = await ut.callAPI(hf , 'hf.imageToImage', args); - return res; - } - /* - inputs = { image: await (await fetch('https://placekitten.com/300/300')).blob() }, - parameters = { candidate_labels: ['cat', 'dog'] }, - */ - const ZeroShotImageClassification = async(inputs, parameters = {}, model = 'openai/clip-vit-large-patch14-336') => { - const args = {inputs, parameters, model}; - const res = await ut.callAPI(hf , 'hf.zeroShotImageClassification', args); - return res; - } - - //......Multimodal - /* - inputs = "That is a happy person", - */ - const FeatureExtraction = async(inputs, model = 'sentence-transformers/distilbert-base-nli-mean-tokens') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.featureExtraction', args); - return res; - } - /* - inputs = { - question: 'How many cats are lying down?', - image: await (await fetch('https://placekitten.com/300/300')).blob() - }, - */ - const VisualQuestionAnswering = async(inputs, model = 'dandelin/vilt-b32-finetuned-vqa') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.visualQuestionAnswering', args); - return res; - } - /* - inputs = { - question: 'Invoice number?', - image: await (await fetch('https://huggingface.co/spaces/impira/docquery/resolve/2359223c1837a7587402bda0f2643382a6eefeab/invoice.png')).blob(), - }, - */ - const DocumentQuestionAnswering = async(inputs, model = 'impira/layoutlm-document-qa') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.documentQuestionAnswering', args); - return res; - } - - //.....Tabular - /* - inputs = { - data: { - "Height": ["11.52", "12.48", "12.3778"], - "Length1": ["23.2", "24", "23.9"], - "Length2": ["25.4", "26.3", "26.5"], - "Length3": ["30", "31.2", "31.1"], - "Species": ["Bream", "Bream", "Bream"], - "Width": ["4.02", "4.3056", "4.6961"] - }, - }, - */ - const TabularRegression = async(inputs, model = 'scikit-learn/Fish-Weight') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.tabularRegression', args); - return res; - } - /* - inputs = { - data: { - "fixed_acidity": ["7.4", "7.8", "10.3"], - "volatile_acidity": ["0.7", "0.88", "0.32"], - "citric_acid": ["0", "0", "0.45"], - "residual_sugar": ["1.9", "2.6", "6.4"], - "chlorides": ["0.076", "0.098", "0.073"], - "free_sulfur_dioxide": ["11", "25", "5"], - "total_sulfur_dioxide": ["34", "67", "13"], - "density": ["0.9978", "0.9968", "0.9976"], - "pH": ["3.51", "3.2", "3.23"], - "sulphates": ["0.56", "0.68", "0.82"], - "alcohol": ["9.4", "9.8", "12.6"] - }, - }, - */ - const TabularClassification = async(inputs, model = 'vvmnnnkv/wine-quality') => { - const args = {inputs, model}; - const res = await ut.callAPI(hf , 'hf.tabularClassification', args); - return res; - } - - //........Custom - /* - inputs = "hello world", - parameters = { - custom_param: 'some magic', - } - */ - const CustomCall = async(inputs, parameters = {}, model = 'my-custom-model') => { - const args = {inputs, parameters, model}; - const res = await ut.callAPI(hf , 'hf.request', args); - return res; - } - /* - inputs = "hello world", - parameters = { - custom_param: 'some magic', - } - */ - const CustomCallStreaming = async(inputs, parameters = {}, model = 'my-custom-model') => { - const args = {inputs, parameters, model}; - return this._makeApiCall('streamingRequest', args); - } - /* - inputs = 'The answer to the universe is', - endpoint = 'https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2' - */ - const CustomInferenceEndpoint = async(inputs, endpoint) => { - const args = { inputs }; - const hfEndpoint = hf.endpoint(endpoint); - try { - const res = await hfEndpoint.textGeneration(args); - return res; - } catch (err) { - throw new Error(`Error occured while triggering "textGeneration" method`, { cause: err }); - } - } - - -module.exports = { - FillMask, - Summarization, - QuestionAnswering, - TableQuestionAnswering , - TextClassification, - TextGeneration, - TextGenerationStream, - TokenClassification, - Translation, - ZeroShotClassification, - SentenceSimilarity, - - AutomaticSpeechRecognition, - AudioClassification, - TextToSpeech, - AudioToAudio, - - ImageClassification, - ObjectDetection, - ImageSegmentation, - ImageToText, - TextToImage, - ImageToImage, - ZeroShotImageClassification, - FeatureExtraction, - - VisualQuestionAnswering, - DocumentQuestionAnswering, - TabularRegression, - TabularClassification, - CustomCall, - CustomCallStreaming, - CustomInferenceEndpoint -}; diff --git a/Huggingface/tests/huggingface-connector.test.js b/Huggingface/tests/huggingface-connector.test.js deleted file mode 100644 index db29479..0000000 --- a/Huggingface/tests/huggingface-connector.test.js +++ /dev/null @@ -1,418 +0,0 @@ -const { readFileSync } = require('node:fs'); -const path = require('node:path'); - -const { - FillMask, - Summarization, - QuestionAnswering, - TableQuestionAnswering, - TextClassification, - TextGeneration, - TextGenerationStream, - TokenClassification, - Translation, - ZeroShotClassification, - SentenceSimilarity, - - AutomaticSpeechRecognition, - AudioClassification, - TextToSpeech, - AudioToAudio, - - ImageClassification, - ObjectDetection, - ImageSegmentation, - ImageToText, - TextToImage, - ImageToImage, - ZeroShotImageClassification, - FeatureExtraction, - - VisualQuestionAnswering, - DocumentQuestionAnswering, - TabularRegression, - TabularClassification, - CustomCall, - CustomCallStreaming, - CustomInferenceEndpoint } = require('../huggingface-connector'); - -const testAudioFile = readFileSync(path.join(__dirname, './test-speech.mp3')); -const testCatFile = readFileSync(path.join(__dirname, './test-cat.jpg')); -const testSeeFile = readFileSync(path.join(__dirname, './test-see.jpeg')); -const testInvoiceFile = readFileSync(path.join(__dirname, './test-invoice.png')); - -// Default timeout -jest.setTimeout(20000); - -describe('HuggingFace Connector', () => { - - test("FillMask", async () => { - const res = await FillMask('[MASK] world!'); - - // console.log(res) - expect(res).toBeArray(); - - const expectedObject = { - score: expect.any(Number), - sequence: expect.any(String), - token: expect.any(Number), - token_str: expect.any(String), - }; - for (const item of res) { - expect(item).toMatchObject(expectedObject); - } - }); - - test("Summarization", async () => { - const res = await Summarization(`The tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey building, and the tallest structure in Paris. - Its base is square, measuring 125 metres (410 ft) on each side. During its construction, the Eiffel Tower surpassed the Washington Monument to become the tallest`); - - // console.log(res); - expect(res).toMatchObject({ summary_text: expect.any(String) }); - }); - - test("QuestionAnswering", async () => { - const res = await QuestionAnswering({ - question: 'What is the capital of France?', - context: 'The capital of France is Paris.' - }); - - // console.log(res); - expect(res).toMatchObject({ - score: expect.any(Number), - start: expect.any(Number), - end: expect.any(Number), - answer: "Paris", - }); - }); - - test("TableQuestionAnswering", async () => { - const res = await TableQuestionAnswering({ - query: 'How many stars does the transformers repository have?', - table: { - Repository: ['Transformers', 'Datasets', 'Tokenizers'], - Stars: ['36542', '4512', '3934'], - Contributors: ['651', '77', '34'], - 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'] - } - }); - - // console.log(res); - expect(res).toMatchObject({ - answer: expect.any(String), - coordinates: [ [ 0, 1 ] ], - cells: [ expect.any(String) ], - aggregator: "AVERAGE", - }); - }); - - test("TextClassification", async () => { - const res = await TextClassification('I like you. I love you.'); - - // console.log(res); - expect(res).toIncludeSameMembers([{ - label: 'POSITIVE', - score: expect.any(Number), - }, { - label: 'NEGATIVE', - score: expect.any(Number), - }]); - }); - - test("TextGeneration", async () => { - const res = await TextGeneration('The answer to the universe is'); - - // console.log(res); - expect(res).toMatchObject({ - generated_text: expect.any(String), - }); - }); - - test("TextGenerationStream", async () => { - const res = await TextGenerationStream('repeat "one two three four"', { max_new_tokens: 250 }); - - // console.log(res); - expect(res).toBeObject(); - }); - - test("TokenClassification", async () => { - const res = await TokenClassification('My name is Sarah Jessica Parker but you can call me Jessica'); - - // console.log(res); - expect(res).toBeArray(); - - const expectedObject = { - start: expect.any(Number), - end: expect.any(Number), - entity_group: expect.any(String), - score: expect.any(Number), - word: expect.any(String), - }; - for (const item of res) { - expect(item).toMatchObject(expectedObject); - } - }); - - test("Translation", async () => { - const res = await Translation('My name is Wolfgang and I live in Amsterdam', {"src_lang": "en_XX", "tgt_lang": "fr_XX"}); - - // console.log(res); - - expect(res).toMatchObject({ - translation_text: 'Mein Name ist Wolfgang und ich lebe in Amsterdam', - }); - }); - - test("ZeroShotClassification", async () => { - const res = await ZeroShotClassification([ - 'Hi, I recently bought a device from your company but it is not working as advertised and I would like to get reimbursed!' - ], { candidate_labels: ['refund', 'legal', 'faq'] }); - - // console.log(res); - - expect(res).toBeArrayOfSize(1); - - expect(res[0]).toMatchObject({ - sequence: 'Hi, I recently bought a device from your company but it is not working as advertised and I would like to get reimbursed!', - labels: [ 'refund', 'faq', 'legal' ], - scores: [ expect.any(Number), expect.any(Number), expect.any(Number) ], - }); - }); - - test("SentenceSimilarity", async () => { - const res = await SentenceSimilarity({ - source_sentence: 'That is a happy person', - sentences: [ - 'That is a happy dog', - 'That is a very happy person', - 'Today is a sunny day' - ] - }); - - // console.log(res); - - expect(res).toIncludeAllMembers([ expect.any(Number), expect.any(Number), expect.any(Number) ]); - }); - - test("AutomaticSpeechRecognition", async () => { - const res = await AutomaticSpeechRecognition(testAudioFile); - - // console.log(res); - expect(res).toMatchObject({ - text: expect.any(String), - }); - }); - - test("AudioClassification", async () => { - const res = await AudioClassification(testAudioFile); - - // console.log(res); - expect(res).toBeArray(); - - const expectedObject = { - label: expect.any(String), - score: expect.any(Number), - }; - for (const item of res) { - expect(item).toMatchObject(expectedObject); - } - }); - - test("TextToSpeech", async () => { - const res = await TextToSpeech('Hello world!'); - - // console.log(res); - expect(res).toBeInstanceOf(Blob); - expect(res.size).toBeNumber(); - expect(res.type).toBe('audio/flac'); - }); - - // TODO: fix test, getting an error "interface not in config.json" - test.skip("AudioToAudio", async () => { - const res = await AudioToAudio(testAudioFile); - - console.log(res); - }); - - test("ImageClassification", async () => { - const res = await ImageClassification(testCatFile); - - // console.log(res); - const expectedObject = { - label: expect.any(String), - score: expect.any(Number), - }; - for (const item of res) { - expect(item).toMatchObject(expectedObject); - } - }); - - test("ObjectDetection", async () => { - const res = await ObjectDetection(testCatFile); - - // console.log(res); - expect(res).toBeArrayOfSize(1); - - expect(res[0]).toMatchObject({ - box: { xmin: expect.any(Number), ymin: expect.any(Number), xmax: expect.any(Number), ymax: expect.any(Number) }, - label: 'cat', - score: expect.any(Number), - }); - }); - - test("ImageSegmentation", async () => { - const res = await ImageSegmentation(testCatFile); - - // console.log(res); - expect(res).toBeArray(); - - const expectedObject = { - score: expect.any(Number), - label: expect.any(String), - mask: expect.any(String), - }; - for (const item of res) { - expect(item).toMatchObject(expectedObject); - } - }); - - test("ImageToText", async () => { - const res = await ImageToText(new Blob([testSeeFile])); - - // console.log(res); - expect(res).toMatchObject({ - generated_text: expect.any(String), - }); - }); - - test("TextToImage", async () => { - const inputs = 'award winning high resolution photo of a giant tortoise/((ladybird)) hybrid, [trending on artstation]'; - const res = await TextToImage(inputs, {negative_prompt: 'blurry'}); - - // console.log(res); - expect(res).toBeInstanceOf(Blob); - expect(res.size).toBeNumber(); - expect(res.type).toBe('image/jpeg'); - }, 60000); - - test("ImageToImage", async () => { - const res = await ImageToImage(new Blob([testSeeFile]), {prompt: "test picture"}); - - // console.log(res); - expect(res).toBeInstanceOf(Blob); - expect(res.size).toBeNumber(); - expect(res.type).toBe('image/jpeg'); - }, 60000); - - test("ZeroShotImageClassification", async () => { - const inputs = { image: new Blob([testCatFile]) }; - const res = await ZeroShotImageClassification(inputs, { candidate_labels: ['cat', 'dog'] }); - - // console.log(res); - const expectedObject = { - score: expect.any(Number), - label: expect.any(String), - }; - for (const item of res) { - expect(item).toMatchObject(expectedObject); - } - }); - - test("FeatureExtraction", async () => { - const res = await FeatureExtraction("That is a happy person"); - - // console.log(res); - expect(res).toSatisfyAll(el => typeof el === 'number'); - }); - - test("VisualQuestionAnswering", async () => { - const inputs = { - question: 'How many cats are lying down?', - image: new Blob([testCatFile]), - }; - const res = await VisualQuestionAnswering(inputs); - - // console.log(res); - expect(res).toMatchObject({ - score: expect.any(Number), - answer: '1', - }); - }); - - test("DocumentQuestionAnswering", async () => { - const inputs = { - question: 'Invoice number?', - image: new Blob([testInvoiceFile]), - }; - const res = await DocumentQuestionAnswering(inputs); - - // console.log(res); - expect(res).toMatchObject({ - score: expect.any(Number), - start: expect.any(Number), - end: expect.any(Number), - answer: 'us-001', - }); - }); - - // TODO: fix test, timeout - test.skip("TabularRegression", async () => { - const inputs = { - data: { - "Height": ["11.52", "12.48", "12.3778"], - "Length1": ["23.2", "24", "23.9"], - "Length2": ["25.4", "26.3", "26.5"], - "Length3": ["30", "31.2", "31.1"], - "Species": ["Bream", "Bream", "Bream"], - "Width": ["4.02", "4.3056", "4.6961"] - }, - }; - const res = await TabularRegression(inputs); - - console.log(res); - }, 60000); - - // TODO: fix test, timeout - test.skip("TabularClassification", async () => { - const inputs = { - data: { - "fixed_acidity": ["7.4", "7.8", "10.3"], - "volatile_acidity": ["0.7", "0.88", "0.32"], - "citric_acid": ["0", "0", "0.45"], - "residual_sugar": ["1.9", "2.6", "6.4"], - "chlorides": ["0.076", "0.098", "0.073"], - "free_sulfur_dioxide": ["11", "25", "5"], - "total_sulfur_dioxide": ["34", "67", "13"], - "density": ["0.9978", "0.9968", "0.9976"], - "pH": ["3.51", "3.2", "3.23"], - "sulphates": ["0.56", "0.68", "0.82"], - "alcohol": ["9.4", "9.8", "12.6"] - }, - }; - const res = await TabularClassification(inputs); - - console.log(res); - }, 60000); - - // TODO: fix test, response is undefined for some reason - test.skip("CustomCall", async () => { - const res = await CustomCall('hello world'); - - console.log(res); - }); - - test("CustomCallStreaming", async () => { - const res = await CustomCallStreaming('hello world'); - - // console.log(res); - expect(res).toBeObject(); - }); - - // TODO: To test this one we need to have own inference endpoint - test.skip("CustomInferenceEndpoint", async () => { - const endpoint = 'https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2'; - const res = await CustomInferenceEndpoint('The answer to the universe is', endpoint); - - console.log(res); - }); -}); \ No newline at end of file diff --git a/Ollama/ollama-connector.js b/Ollama/ollama-connector.js deleted file mode 100644 index 52cab18..0000000 --- a/Ollama/ollama-connector.js +++ /dev/null @@ -1,42 +0,0 @@ -require('dotenv').config(); -const ut = require(process.cwd() + '/utilities'); -const { Ollama } = require('ollama'); - - -let ollama; - try { - ollama = new Ollama({ host: 'http://localhost:11434' }) - } - catch (error) { - throw new Error('Ollama is not running') - process.exit(1) - } - - /* - (messages = [{ role: 'user', content: 'Why is the sky blue?' }], - - */ - const completion = async (messages = [], - system = `You are a useful assistant. You can answer questions, provide information, and help with tasks.`, - model = 'llama2', - stream = false) => { - - const response = await ut.callAPI(ollama, 'ollama.chat', { - messages:[ - { - "role": "system", - "content": system - }, - ...messages - ], - model - }) - - return response.message; - } - - - -module.exports = { - completion -} \ No newline at end of file diff --git a/Ollama/tests/ollama-connector.test.js b/Ollama/tests/ollama-connector.test.js deleted file mode 100644 index aaf67d2..0000000 --- a/Ollama/tests/ollama-connector.test.js +++ /dev/null @@ -1,22 +0,0 @@ -const { - completion - -} = require('../ollama-connector'); -console.log(completion) - -// Default timeout -jest.setTimeout(20000); - -// Ollama -describe('Ollama Connector', () => { - test('Completion', async () => { - const data = await completion([{ - "role": "user", - "content": "how are you doing?" - }]); - // console.log(data) - expect(data.message.role).toBe('assistant'); - }); -}) - - diff --git a/OpenAI/openai-config.js b/OpenAI/openai-config.js deleted file mode 100644 index 66f6a17..0000000 --- a/OpenAI/openai-config.js +++ /dev/null @@ -1,15 +0,0 @@ -// const { nlp } = require("./OpenAI/openai-connector"); -module.exports = { - DEFAULT_MODELS:{ - completions:'gpt-4-turbo-2024-04-09', - embedding:'text-embedding-3-small', - classification:'text-moderation-007', - fineTune:'gpt-3.5-turbo-0125', - textToSpeech:'tts-1', - speech:'whisper-1', - vision:'gpt-4-vision-preview', - imageCreate:'dall-e-3', - image:'dall-e-2' - }, - DEFAULT_VOICE : 'onyx' -} \ No newline at end of file diff --git a/OpenAI/openai-connector.js b/OpenAI/openai-connector.js deleted file mode 100644 index e5692bc..0000000 --- a/OpenAI/openai-connector.js +++ /dev/null @@ -1,972 +0,0 @@ -require('dotenv').config() -const ut = require(process.cwd() + '/utilities'); - -const OpenAI = require ("openai"); -const fs = require ("fs"); -const path = require('path'); - - - - -const util = require('util') -const stream = require('stream') -const { spawn } = require('node:child_process'); -const pipeline = util.promisify(stream.pipeline); -const sharp = require('sharp'); - -const {DEFAULT_MODELS, DEFAULT_VOICE} = require('./openai-config'); -const {tokens, price} = require('./openai-tokens-price'); -const openai = new OpenAI({apiKey: process.env['OPENAI_API_KEY']}); - - -// console.log(openai.name, openai.constructor.name); -// process.exit(0); - - -class Chat{ - //, temperature = 0.7, topP = 1, frequencyPenalty = 0, presencePenalty = 0, stop = ["\n", ""] - constructor({system, model = DEFAULT_MODELS.completions, tools, maxTokens = 1000, maxPrice = 0.1}){ - this.system = system; - this.model = model; - this.tools = tools; - this.maxTokens = maxTokens; - // this.maxPrice = maxPrice; - - this.messages = []; - this.tokens = 0; - this.price = 0; - - // throw new Error(`Max ${maxTokens} tokens exceeded`); - } - async message ({text}) { - const tokensNumber = tokens.count({text, model:this.model}); - - if (this.tokens + tokensNumber >= this.maxTokens){ - throw new Error(`Max ${this.maxTokens} tokens exceeded`); - } - - const res = await language.generate({text, messages:this.messages, system:this.system, model:this.model, tools:this.tools}); - - this.messages = res.messages; - this.tokens += res.usage.total_tokens; - this.price += res.usage.total_price; - - return res.message; - } - -/* - "text" argument - in case we do conversion on front end - */ - async voiceMessage ({text, inputFilePath, outputFilePath, voice = DEFAULT_VOICE, returnIntermediateResult = false}) { - let inputText, start = ut.measureTime(); - if (inputFilePath){ - inputText = await speech.speechToText({pathToFile:inputFilePath}); - } - else { - inputText = text; - } - const outputText = await this.message({text:inputText}); - if (returnIntermediateResult) return {inputText, outputText, outputFilePath, executionTime:ut.measureTime(start)}; - - const buffer = await speech.textToSpeech({text:outputText, pathToFile:outputFilePath, voice}); - - return {inputText, outputText, outputFilePath, executionTime:ut.measureTime(start)}; - } - - async voiceAnswer ({inputText, outputText, outputFilePath, voice = DEFAULT_VOICE}) { - const start = ut.measureTime(); - const buffer = await speech.textToSpeech({text:outputText, pathToFile:outputFilePath, voice}); - return {inputText, outputText, outputFilePath, executionTime:ut.measureTime(start)}; - } - -} - -class Assistant { - - constructor({assistant_id, thread_id, model = DEFAULT_MODELS.completions, maxTokens = 1000, maxPrice = 0.1}){ - this.id = assistant_id; - this.thread_id = thread_id; - // this.model = model; - // this.maxTokens = maxTokens; - // this.maxPrice = maxPrice; - - this.messages = []; - // this.tokens = 0; - // this.price = 0; - - } - - message({text}) { - - } - - - - -} - - - - - - - //......Natural Language Processing (language)....... - - -/* -you should accumulate the messages in the conversation and send them all at once to the completion endpoint. - -Example of messages: - [{ - "role": "user", - "content": "Hello, I'm a user." - },{ - "role": "assistant", - "content": "Hello, how can I help you?" - },{ - "role": "user", - "content": "Why roses are red?" - }] - - Models: - gpt-3.5-turbo - cheaper, less advanced - gpt-4-turbo-2024-04-09 - more advanced, more expensive - -*/ - -const defineTools = (params = []) => { - const tools = [], functions = {}; - let tool; - for (f of params){ - let name = f.name || f.fn.name.replace(/([A-Z])/g, ($0, $1)=>'_' + $1.toLowerCase()); - tool = { - type: "function", - function: { - name, - description: f.description, - parameters: { - type: "object", - properties: f.properties, - required: f.required, - }, - }, - }; - tools.push(tool); - functions[name] = {fn:f.fn, scope:f.scope}; - // if (tool.type === 'code_interpreter'){ - // functions.push({ - // "type": "code_interpreter", - // "code": tool.code - // }); - // } - } - return {tools, functions} -} - - - const language = { - generate: async({text, - messages = [], - system = `You are a useful assistant. You can answer questions, provide information, and help with tasks.`, - model = DEFAULT_MODELS.completions, - tools = [], - tool_choice = "auto" - }) => { - - - - const currentMessages = [ - ...messages, - { - "role": "user", - "content": text - }]; - - const params = { - messages:[{ - "role": "system", - "content": system - }, - ...currentMessages - ], - model: model - }; - - let defs = {tools:[], functions:{}}; - if (Array.isArray(tools) && tools.length){ - defs = defineTools(tools); - params.tools = defs.tools; - params.tool_choice = tool_choice; - } - // return params; - let completion = await ut.callAPI(openai.chat.completions, 'openai.chat.completions.create', params); - if(completion.error) return completion; - - let responseMessage = completion.choices[0].message; - - const usages = [completion.usage]; - - if (responseMessage.tool_calls) { - - params.messages.push(responseMessage); - - for (const toolCall of responseMessage.tool_calls) { - const functionName = toolCall.function.name; - const functionToCall = defs.functions[functionName]; - if (!functionToCall) throw new Error(`Function ${functionName} not found`); - - const {fn, scope}= functionToCall; - const args = JSON.parse(toolCall.function.arguments); - console.log({functionName, functionToCall, args}); - const functionResponse = await fn.call(scope, args) - console.log({functionResponse}); - - params.messages.push({ - tool_call_id: toolCall.id, - role: "tool", - name: functionName, - content: functionResponse, - }); - } - - completion = await ut.callAPI(openai.chat.completions, 'openai.chat.completions.create', params); - if(completion.error) return completion; - responseMessage = completion.choices[0].message; - usages.push(completion.usage); - - // console.log({completion, responseMessage}) - } - - currentMessages.push(responseMessage); - const usageAndPrices = price.calculateByUsage({purpose:'completion', model, usages}); - - return {message:responseMessage.content, messages:currentMessages, usage:usageAndPrices}; - }, - - embedding: async({text = "Sample text", model = DEFAULT_MODELS.embedding}) => { - const response = await ut.callAPI(openai.embeddings, 'openai.embeddings.create', { - model, - input: text, - encoding_format: "float", - }); - if(response.error) return response; - const usageAndPrices = price.calculateByUsage({purpose:'embedding', model, usage:response.usage}) - return {embedding:response.data[0].embedding, usage:usageAndPrices}; - }, - - classification: async({text, model = DEFAULT_MODELS.classification}) => { - - const response = await ut.callAPI(openai.moderations, 'openai.moderations.create', { - input: text - }); - if(response.error) return response; - return response.results[0]; - - } - - } - - const files = { - - countFileTokens: async({pathToFile, model = DEFAULT_MODELS.completions}) => { - let tokensNumber = 0 - await ut.processFileLineByLine(pathToFile, ({line, index}) => { - tokensNumber += tokens.count({text:line, model}); - }, (args) => {console.log(args)}); - return tokensNumber; - }, - - create: async({pathToFile, purpose = 'fine-tune'}) => { - // purposes = "fine-tune", "assistants" - const response = await ut.callAPI(openai.files, 'openai.files.create', { file: fs.createReadStream(pathToFile), purpose }); - return response; - }, - - list: async() => { - const response = await ut.callAPI(openai.files, 'openai.files.list'); - if(response.error) return response; - return response.data; - }, - - retrieve: async({file_id}) => { - const response = await ut.callAPI(openai.files, 'openai.files.retrieve', file_id); - return response; - }, - - content : async({file_id}) => { - const file = await ut.callAPI(openai.files, 'openai.files.retrieveContent', file_id); - return file; - }, - - del: async({file_id}) => { - const file = await ut.callAPI(openai.files, 'openai.files.del', file_id); - return file; - } - } - - const fineTune = { - - // hyperparameters = - // batch_size - // string or integer Optional Defaults to auto - // Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance. - - // learning_rate_multiplier string or number Optional Defaults to auto - // Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting. - - // n_epochs - // string or integer Optional Defaults to auto - // The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset. - create: async({pathToFile, training_file, - hyperparameters = {batch_size:'auto', learning_rate_multiplier:'auto', n_epochs:'auto'}, - suffix = '', model = DEFAULT_MODELS.fineTune, deleteFile = false, maxTokens = 0}) => { - if (pathToFile) { - const epochs = hyperparameters.n_epochs != 'auto' ? hyperparameters.n_epochs : 1; - const tokens = await files.countFileTokens({pathToFile}); - if (maxTokens && tokens*epochs > maxTokens) { - throw new Error(`Max tokens ${maxTokens} exceeded ${tokens}`); - } - const file = await files.create({pathToFile}); - if (!file || !file.id) return console.error('Error creating file'); - training_file = file.id; - } - const params = { training_file, suffix, model }; - if (hyperparameters) params.hyperparameters = hyperparameters; - if (suffix) params.suffix = suffix; - - const response = await ut.callAPI(openai.fineTuning.jobs, 'openai.fineTuning.jobs.create', params); - if(response.error) return response; - if (deleteFile) await files.del({training_file}); - - return response; - }, - - list: async() => { - const response = await ut.callAPI(openai.fineTuning.jobs, 'openai.fineTuning.jobs.list'); - if(response.error) return response; - return response.data; - // for await (const fineTune of list) { - // console.log(fineTune); - // } - }, - - events: async({id, limit=2}) => { - const list = await ut.callAPI(openai.fineTuning, 'openai.fineTuning.list_events', id, limit); - - // for await (const fineTune of list) { - // console.log(fineTune); - // } - return list; - }, - retrieve: async({id}) => { - const res = await ut.callAPI(openai.fineTuning.jobs, 'openai.fineTuning.jobs.retrieve', id); - return res; - }, - cancel: async({id}) => { - const fineTune = await ut.callAPI(openai.fineTuning.jobs, 'openai.fineTuning.jobs.cancel', id); - return fineTune; - } - - } - - const models = { - list: async() => { - const response = await ut.callAPI(openai.models, 'openai.models.list'); - if(response.error) return response; - return response.data; - // for await (const fineTune of list) { - // console.log(fineTune); - // } - }, - retrieve: async({model_id}) => { - const response = await ut.callAPI(openai.models, 'openai.models.retrieve', model_id); - return response; - }, - del: async({model_id}) => { - const response = await ut.callAPI(openai.models, 'openai.models.del', model_id); - return response; - } - } - -const speech = { - /* - voices: alloy, echo, fable, onyx, nova, and shimmer - speed: 0.25 - 4.0 - model: tts-1, tts-1-hd - */ - - textToSpeech: async ({text, pathToFile = "./tests/test-speech.mp3", voice = DEFAULT_VOICE, speed = 1.0, model = DEFAULT_MODELS.textToSpeech}) => { - - - - const speechFile = path.resolve(pathToFile); - const mp3 = await ut.callAPI(openai.audio.speech, 'openai.audio.speech.create', - { - input: text, - voice, - speed, - model - }); - if(mp3.error) return mp3; - const buffer = Buffer.from(await mp3.arrayBuffer()); - await fs.promises.writeFile(speechFile, buffer); - return buffer; - }, - - - speechToText: async({pathToFile = "./tests/test-speech.mp3", model = DEFAULT_MODELS.speech}) => { - - const buffer = await fs.promises.readFile(path.resolve(pathToFile)); - const transcription = await ut.callAPI(openai.audio.transcriptions, 'openai.audio.transcriptions.create', { - file: await OpenAI.toFile(buffer, path.basename(pathToFile)), - model, - }); - if(transcription.error) return transcription; - return transcription.text; - }, - - - speechTranslation: async({pathToFile = "./tests/test-speech.mp3", model = DEFAULT_MODELS.speech}) => { - - const translation = await ut.callAPI(openai.audio.translations, 'openai.audio.translations.create', { - file: fs.createReadStream(pathToFile), - model, - }); - if(translation.error) return translation; - return translation.text; - } -} - -const images = { - - - -/* - - prompt: "a white siamese cat", - model: "dall-e-3", - n: 1, (dalle-3 always generates only one image per request) - size: "1024x1024", - - */ - create: async ({text, saveAs = '', size = "1024x1024", quality="standard", n = 1, model = DEFAULT_MODELS.imageCreate}) => { - //quality: "standard", "hd" - // return console.log(pathToFile.replace(/^\./, '')); - - const response = await ut.callAPI(openai.images, 'openai.images.generate', { - prompt:text, - size, - quality, - n, - model - }); - if(response.error) return response; - const url = response?.data[0]?.url; - // const url = `https://oaidalleapiprodscus.blob.core.windows.net/private/org-0W2DSt5sTB0F2NuqbNIHHcTg/user-m7HmI4bqRdNZxYTHJlFPLN9P/img-nYmIPv103J4yZAw29MTr7N7x.png?st=2024-03-24T17%3A16%3A00Z&se=2024-03-24T19%3A16%3A00Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-03-23T21%3A30%3A02Z&ske=2024-03-24T21%3A30%3A02Z&sks=b&skv=2021-08-06&sig=ByJlRNd/IQsOrgmruP8u/oyjWmKE/PDW4tG9E1YuoRo%3D`; - if (url && saveAs.length) { - try { - await ut.saveFileFromWeb(url, saveAs); - } catch (error) { - console.error('Error saving file:', error); - } - } - return {url, local:saveAs.replace(/^\./, '')}; - - }, - /* - accepts only .png RGBA images - */ - edit: async ({text, pathToFile, pathToMask = '', saveAs = '', size = "1024x1024", n = 1, model = DEFAULT_MODELS.image}) => { - - const params = { - prompt:text, - image:fs.createReadStream(pathToFile), - model, - n, - size - }; - if (pathToMask.length) params.mask = fs.createReadStream(pathToMask); - - const response = await ut.callAPI(openai.images, 'openai.images.edit', params); - if(response.error) return response; - const url = response.data[0].url; - if (url && saveAs.length) { - try { - await ut.saveFileFromWeb(url, saveAs); - } catch (error) { - return console.error('Error saving file:', error); - } - } - return {url, local:saveAs.replace(/^\./, '')}; - }, - - variation: async ({pathToFile, saveAs = '', size = "1024x1024", n = 1, model = DEFAULT_MODELS.image}) => { - - const response = await ut.callAPI(openai.images, 'openai.images.createVariation', { - image:fs.createReadStream(pathToFile), - model, - n, - size - }); - if(response.error) return response; - const url = response.data[0].url; - if (url && saveAs.length) { - try { - await ut.saveFileFromWeb(url, saveAs); - } catch (error) { - return console.error('Error saving file:', error); - } - } - return {url, local:saveAs.replace(/^\./, '')}; - - } -} - -const recognition = { - - - - image: async ({url, pathToFile, prompt = "What’s in this image?", detail = "auto", max_tokens = 300, model = DEFAULT_MODELS.vision}) => { - - let imageURL; - if (url){ - imageURL = url; - } - else if (pathToFile){ - const data = await fs.promises.readFile(pathToFile); - let base64Image = Buffer.from(data, 'binary').toString('base64'); - imageURL = `data:image/jpeg;base64, ${base64Image}`; - } - else { - throw new Error('No image provided'); - } - const response = await ut.callAPI(openai.chat.completions, 'openai.chat.completions.create', { - model, - messages: [ - { - role: "user", - content: [ - { type: "text", text: prompt }, - { - type: "image_url", - image_url: { - "url": imageURL, - detail //low, high, or auto - }, - }, - ], - }, - ], - max_tokens - }); - if(response.error) return response; - return {message:response.choices[0].message.content, usage:response.usage}; - }, - /* - Requires ffmpeg installed - May exceed the max tokens per minutes limit - */ - video: async ({pathToFile, outputDir, max_tokens = 300, frameRate = 1, model = DEFAULT_MODELS.vision}) => { - - await ut.cleanDirectory(outputDir); - await ut.extractVideoFrames(pathToFile, outputDir, frameRate); - const files = await fs.promises.readdir(outputDir); - const content = [{ type: "text", text: "These are frames from a video. Generate a compelling description of the video." }]; - for (file of files){ - const data = await fs.promises.readFile(path.join(outputDir, file)); - const base64Image = Buffer.from(data, 'binary').toString('base64'); - const imageURL = `data:image/jpeg;base64, ${base64Image}`; - content.push({"type": "image_url", "image_url": {"url":imageURL}}); - } - - const response = await ut.callAPI(openai.chat.completions, 'openai.chat.completions.create', { - model, - messages: [ - { - role: "user", - content, - }, - ], - max_tokens - }); - if(response.error) return response; - return {message:response.choices[0].message.content, usage:response.usage} - - - } - -} - - - - -//.........ASSISTANTS............. -/* -The main advantage of an assistant in comparison to completeon - you don't have to send all the messages to OpenAI for each user interaction. -You can send only a new message to the assistant and OpenAI will keep and manage the context of the conversation. -The disadvantage is - you can't add or remove some other content to the conversation, like info from other models or any other data. -And it's still beta - sometimes it does not behave according to instructions -*/ - -/* - name = "Math Tutor", - instructions = "You are a personal math tutor. Write and run code to answer math questions.", - tools = [{ type: "code_interpreter" }], - model = DEFAULT_MODELS.completions - -*/ - -const assistants = { - - create: async ({ - name , - instructions, - tools = [], - model = DEFAULT_MODELS.completions}) => { - - // tools = {type:code_interpreter}, {type:retrieval}, or {type:function, function:{name, description, parameters:{type, properties, required}}} - - const response = await ut.callAPI(openai.beta.assistants, 'openai.beta.assistants.create', - { - name, - instructions, - tools, - model - }); - return response; - }, - - list: async() => { - const response = await ut.callAPI(openai.beta.assistants, 'openai.beta.assistants.list'); - if(response.error) return response; - return response.data; - }, - - - retrieve: async({assistant_id}) => { - const response = await ut.callAPI(openai.beta.assistants, 'openai.beta.assistants.retrieve', assistant_id); - return response; - }, - - update: async ({assistant_id, name, instructions, tools, model, file_ids}) => { - const current_assistant = await assistants.retrieve({assistant_id}); - const args = {name, instructions, tools, model, file_ids}; - const props = {} - for (let key in args){ - props[key] = (typeof args[key] === 'undefined') ? current_assistant[key] : args[key]; - } - const response = await ut.callAPI(openai.beta.assistants, 'openai.beta.assistants.update', assistant_id, props); - return response; - }, - - del: async ({assistant_id}) => { - const response = await ut.callAPI(openai.beta.assistants, 'openai.beta.assistants.del', assistant_id); - return response; - }, - - files:{ - create: async ({assistant_id, file_id}) => { - //purpose should be "assistants" - const file = await ut.callAPI(openai.beta.assistants.files, 'openai.beta.assistants.files.create', assistant_id, {file_id}) - return file; - }, - list: async ({assistant_id}) => { - const files = await ut.callAPI(openai.beta.assistants.files, 'openai.beta.assistants.files.list', assistant_id); - if(files.error) return files; - return files.data; - }, - retrieve: async ({assistant_id, file_id}) => { - const file = await ut.callAPI(openai.beta.assistants.files, 'openai.beta.assistants.files.retrieve', assistant_id, file_id); - return file; - }, - del: async ({assistant_id, file_id}) => { - const file = await ut.callAPI(openai.beta.assistants.files, 'openai.beta.assistants.files.del', assistant_id, file_id); - return file; - } - - }, - - threads:{ - - create: async({messages = []}) => { - const response = await ut.callAPI(openai.beta.threads, 'openai.beta.threads.create'); - return response; - }, - - createAndRun: async({assistant_id, thread = {messages:[]}}) => { - const response = await ut.callAPI(openai.beta.threads, 'openai.beta.threads.createAndRun', {assistant_id, thread}); - return response; - }, - - /* - id = 'thread_D1Fc45AQAhZsywNdSAGReFpM' - */ - retrieve: async ({thread_id}) => { - const response = await ut.callAPI(openai.beta.threads, 'openai.beta.threads.retrieve', thread_id); - return response; - }, - - update: async ({thread_id, params = {}}) => { - const response = await ut.callAPI(openai.beta.threads, 'openai.beta.threads.update', thread_id, params); - return response; - }, - - - del: async({thread_id}) => { - const response = await ut.callAPI(openai.beta.threads, 'openai.beta.threads.del', thread_id); - return response; - }, - - - messages:{ - - create: async({thread_id, role = 'user', content = ''}) => { - // return console.log({thread_id, role, content}); - const response = await ut.callAPI(openai.beta.threads.messages, 'openai.beta.threads.messages.create', - thread_id, - { - role, - content - } - ); - return response; - }, - list: async({thread_id, clean = false}) => { - - const response = await ut.callAPI(openai.beta.threads.messages, 'openai.beta.threads.messages.list', - thread_id - ); - if(response.error) return response; - if (clean) { - const messages = []; - for (message of response.data){ - messages.push(message.content); - } - // console.log({message0:messages[0][0].text, message1:messages[1][0].text}); - return messages; - } - return response; - }, - listFiles: async({thread_id, message_id}) => { - const response = await ut.callAPI(openai.beta.threads.messages, 'openai.beta.threads.messages.files.list', - thread_id, - message_id - ); - if(response.error) return response; - return response.data; - - }, - - retrieve: async({thread_id, message_id}) => { - const response = await ut.callAPI(openai.beta.threads.messages, 'openai.beta.threads.messages.retrieve', - thread_id, - message_id - ); - return response; - - }, - retrieveFile: async({thread_id, message_id, file_id}) => { - const response = await ut.callAPI(openai.beta.threads.messages.files, 'openai.beta.threads.messages.files.retrieve', - thread_id, - message_id, - file_id - ); - return response; - }, - update: async({thread_id, message_id, params = {metadata:{ metadata: { - modified: "true", - user: "test" - } - } - } - }) => { - const response = await await ut.callAPI(openai.beta.threads.messages, 'openai.beta.threads.messages.update', - thread_id, - message_id, - params) - return response; - } - }, - - runs:{ - create: async({thread_id, assistant_id}) => { - const response = await ut.callAPI( openai.beta.threads.runs, 'openai.beta.threads.runs.create', - thread_id, - { - assistant_id - } - ); - return response; - }, - list: async({thread_id}) => { - const response = await ut.callAPI(openai.beta.threads.runs, 'openai.beta.threads.runs.list', thread_id); - if(response.error) return response; - return response.data; - }, - retrieve: async({thread_id, run_id}) => { - const response = await ut.callAPI(openai.beta.threads.runs, 'openai.beta.threads.runs.retrieve', - thread_id, - run_id - ); - return response; - }, - - - // .....logs - listRunSteps: async({thread_id, run_id, limit=20, clean}) => { - const response = await ut.callAPI(openai.beta.threads.runs.steps, 'openai.beta.threads.runs.steps.list', - thread_id, - run_id - ); - if(response.error) return response; - if (clean) { - const steps = []; - for (log of response.data){ - steps.push({details:log.step_details, usage:log.usage}); - } - return steps; - } - return response; - }, - retrieveRunStep: async({thread_id, run_id, step_id}) => { - const response = await ut.callAPI(openai.beta.threads.runs.steps, 'openai.beta.threads.runs.steps.retrieve', - thread_id, - run_id, - step_id - ); - return response; - }, - update: async({thread_id, run_id, props = {}}) => { - const response = await ut.callAPI(openai.beta.threads.runs, 'openai.beta.threads.runs.update', thread_id, run_id, props); - return response; - }, - /* - When a run has the status: "requires_action" and required_action.type is submit_tool_outputs, - this endpoint can be used to submit the outputs from the tool calls once they're all completed. - All outputs must be submitted in a single request. - Like this - { - tool_outputs: [ - { - tool_call_id: "call_001", - output: "70 degrees and sunny.", - }, - ], - } - */ - submitToolOutput: async({thread_id, run_id, output = {tool_outputs: []}}) => { - const response = await ut.callAPI(openai.beta.threads.runs.steps.tools, 'openai.beta.threads.runs.steps.tools.submit', - thread_id, - run_id, - output - ); - return response; - }, - - cancel: async({thread_id, run_id}) => { - const response = await ut.callAPI(openai.beta.threads.runs, 'openai.beta.threads.runs.cancel', - thread_id, - run_id - ); - return response; - } - - - } - - } -} - - - - - -module.exports = { - - - Chat, //class Chat - // chat.message({text}) => {message, messages, usages} => string - // chat.voiceMessage({inputFilePath, outputFilePath, voice}) => {inputText, outputText, outputFilePath} - Assistant, //class Assistant - tokens, - // tokens.count({text, model}) => integer - price, - // price.estimate({purpose, model, text = '', pathToFile = '', - // options = {type: 'prompt', detail:'low', resolution:"1024x1024", quality:'standard'}}) => {price:0, tokens:0, options} - // price.calculate({purpose, model, usage}) => { - // prompt_token, - // completion_token, - // total_tokens, - // prices: { prompt, completion, total }, - // strings: { prompt, completion, total } - // } - language, - // language.generate({text, messages, system, model, tools, tool_choice}) => {message, messages, usages} - // language.embedding({text, model}) => {embedding, usage} - // language.classification({text, model}) => response - files, - // files.create({pathToFile, purpose}) => response - // files.list() => data (array) - // files.retrieve({file_id}) => response - // files.content({file_id}) => response - // files.del({file_id}) => response - fineTune, - // fineTune.create({pathToFile, training_file, hyperparameters, suffix, model, deleteFile, maxTokens}) => response - // fineTune.list() => data (array) - // fineTune.events({id, limit}) => response - does not work - // fineTune.retrieve({id}) => response - // fineTune.cancel{({id}) => response - models, - // models.list() => response - // models.retrieve({model_id}) => response - // models.del({model_id}) => response - images, - // images.create({text, pathToFile, size, quality, n, model}) => {url, local} - // images.edit({text, pathToFile, pathToMask, size, n, model}) => response - // images.variation({pathToFile, size, n, model}) => response - speech, - // speech.textToSpeech({text, pathToFile, voice, model}) => buffer - // speech.speechToText({pathToFile, model}) => string - // speech.speechTranslation({pathToFile, model}) => string // does not translate - recognition, - // recognition.image({url, pathToFile, prompt, max_tokens, model}) => {message, usage} - // recognition.video({pathToFile, outputDir, max_tokens, model}) => {message, usage} - assistants, //test - // assistants.create({name, instructions, tools, model}) => response - // assistants.list() => data (array) - // assistants.retrieve({assistant_id}) => response - // assistants.update({assistant_id, name, instructions, tools, model, file_ids}) => response - // assistants.del({assistant_id}) => response - - // assistants.files.create({file_id}) => response - // assistants.files.list({assistant_id}) => data (array) - // assistants.files.retrieve({assistant_id, file_id}) => response - // assistants.files.del({assistant_id, file_id}) => response - - // assistants.threads.create({messages = []}) => response - // assistants.threads.createAndRun({assistant_id, thread = {messages:[]}}) => response - // assistants.threads.retrieve({thread_id}) => response - // assistants.threads.update({thread_id, messages}) => response - // assistants.threads.del({thread_id}) => response - - // assistants.threads.messages.create({thread_id, role, content}) => response - // assistants.threads.messages.list({thread_id, clean}) => data(array) - // assistants.threads.messages.listFiles({thread_id, message_id}) => data (array) //does not work - // assistants.threads.messages.retrieve({thread_id, message_id}) => response - // assistants.threads.messages.retrieveFile({thread_id, message_id, file_id}) => response - // assistants.threads.messages.update({thread_id, message_id, user_id}) => response - - // assistants.threads.runs.create({thread_id, assistant_id}) => response - // assistants.threads.runs.list({thread_id}) => response - // assistants.threads.runs.retrieve({thread_id, run_id}) => response - // assistants.threads.runs.listRunSteps({thread_id, run_id, limit, clean}) => response - // assistants.threads.runs.retrieveRunStep({thread_id, run_id, step_id}) => response - // assistants.threads.runs.update({thread_id, run_id, data}) => response - // assistants.threads.runs.submitToolOutput({thread_id, run_id, output}) => response - // assistants.threads.runs.cancel({thread_id, run_id}) => response - - price - // price.calculate({model, input, type}) => number -}; - - diff --git a/OpenAI/openai-data.js b/OpenAI/openai-data.js deleted file mode 100644 index cae32c2..0000000 --- a/OpenAI/openai-data.js +++ /dev/null @@ -1,126 +0,0 @@ -const models = { - completion:{ - 'gpt-4-turbo-2024-04-09':{prices:{prompt:10.00, completion:30.00}}, - 'gpt-4-1106-preview':{prices:{prompt:10.00, completion:30.00}}, - 'gpt-3.5-turbo-0125':{prices:{prompt:0.50, completion:1.50}} - }, - //...No models in Tiktoken - embedding:{ - 'text-embedding-3-small': {prices:{prompt:0.02}, dimension:1536}, - 'text-embedding-3-large': {prices:{prompt:0.13}, dimension:3072}, - 'text-embedding-ada-002':{prices:{prompt:0.10}, dimension:1536} - }, - recognition:{ - 'gpt-4-vision-preview':{tokens:{base:85, block:170}, prices:{prompt:10.00, completion:30.00}}, - }, - images:{ - 'dall-e-3':{prices:{standard:{'1024×1024':0.040, '1024×1792':0.080, '1792×1024':0.080}, hd:{'1024×1024':0.080, '1024×1792':0.120, '1792×1024':0.120}}}, - 'dall-e-2':{prices:{standard:{'256×256':0.016, '512×512':0.018, '1024×1024': 0.020}}} - }, - fineTune:{ - 'gpt-3.5-turbo':{prices:{training:8.00, prompt:3.00, completion:6.00}}, - 'davinci-002':{prices:{training:6.00, prompt:12.00, completion:12.00}}, - 'babbage-002':{prices:{training:0.40, prompt:1.60, completion:1.60}} - }, - speech: { - 'tts-1':{prices:{prompt:15.00}}, - 'tts-1-hd':{prices:{prompt:30.00}}, - 'whisper-1':{prices:{minute:0.006}}, - }, - assistants:{ - 'assistant':{prices:{'code-interpreter-session': 0.03, 'retrieval-GB-day':0.20}} - } - }; - -/* -Rate limits are measured in five ways: -RPM (requests per minute), -RPD (requests per day), -TPM (tokens per minute), -TPD (tokens per day), -and IPM (images per minute). -Rate limits can be hit across any of the options depending on what occurs first. -For example, you might send 20 requests with only 100 tokens to the ChatCompletions endpoint and that would fill your limit (if your RPM was 20), -even if you did not send 150k tokens (if your TPM limit was 150k) within those 20 requests. - -Batch API queue limits are calculated based on the total number of input tokens queued for a given model. - -Tokens from pending batch jobs are counted against your queue limit. Once a batch job is completed, its tokens are no longer counted against that model's limit. - -Other important things worth noting: - -Rate limits are defined at the organization level and at the project level, not user level. -Rate limits vary by the model being used. -Limits are also placed on the total amount an organization can spend on the API each month. These are also known as "usage limits". - -*/ - -const tiers = { - - free: {name:'Free', price:0.00, description:'Free tier with limited access', limits:{ - 'gpt-3.5-turbo': { RPM: 3, RPD: 200, TPM: 40000, BATCH_QUEUE_LIMIT: 200000 }, - 'text-embedding-3-small': { RPM: 3, RPD: 200, TPM: 150000, BATCH_QUEUE_LIMIT: null }, - 'whisper-1': { RPM: 3, RPD: 200, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1': { RPM: 3, RPD: 200, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-2': { RPM: '5 img/min', RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-3': { RPM: '1 img/min', RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null } - }}, - tier1: {name:'Tier 1', price:10.00, description:'Paid tier with access to all models', limits: { - 'gpt-4-turbo': { RPM: 500, RPD: null, TPM: 300000, BATCH_QUEUE_LIMIT: 900000 }, - 'gpt-4': { RPM: 500, RPD: 10000, TPM: 10000, BATCH_QUEUE_LIMIT: 100000 }, - 'gpt-3.5-turbo': { RPM: 3500, RPD: 10000, TPM: 60000, BATCH_QUEUE_LIMIT: 200000 }, - 'text-embedding-3-large': { RPM: 500, RPD: 10000, TPM: 1000000, BATCH_QUEUE_LIMIT: null }, - 'whisper-1': { RPM: 50, RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1': { RPM: 50, RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1-hd': { RPM: 3, RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-2': { RPM: '5 img/min', RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-3': { RPM: '5 img/min', RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null } - }}, - tier2: {name:'Tier 2', price:20.00, description:'Paid tier with access to all models and additional features', limits:{ - 'gpt-4-turbo': { RPM: 5000, TPM: 450000, BATCH_QUEUE_LIMIT: 1350000 }, - 'gpt-4': { RPM: 5000, TPM: 40000, BATCH_QUEUE_LIMIT: 200000 }, - 'gpt-3.5-turbo': { RPM: 3500, TPM: 80000, BATCH_QUEUE_LIMIT: 400000 }, - 'text-embedding-3-large': { RPM: 500, TPM: 1000000, BATCH_QUEUE_LIMIT: null }, - 'whisper-1': { RPM: 50, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1': { RPM: 50, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1-hd': { RPM: 5, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-2': { RPM: '50 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-3': { RPM: '7 img/min', TPM: null, BATCH_QUEUE_LIMIT: null } - }}, - tier3: {name:'Tier 3', price:30.00, description:'Paid tier with access to all models and additional features', limits:{ - 'gpt-4-turbo': { RPM: 5000, TPM: 600000, BATCH_QUEUE_LIMIT: 40000000 }, - 'gpt-4': { RPM: 5000, TPM: 80000, BATCH_QUEUE_LIMIT: 5000000 }, - 'gpt-3.5-turbo': { RPM: 3500, TPM: 160000, BATCH_QUEUE_LIMIT: 10000000 }, - 'text-embedding-3-large': { RPM: 5000, TPM: 5000000, BATCH_QUEUE_LIMIT: null }, - 'whisper-1': { RPM: 100, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1': { RPM: 100, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1-hd': { RPM: 7, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-2': { RPM: '100 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-3': { RPM: '7 img/min', TPM: null, BATCH_QUEUE_LIMIT: null } - }}, - tier4: {name:'Tier 4', price:40.00, description:'Paid tier with access to all models and additional features', limits:{ - 'gpt-4-turbo': { RPM: 10000, TPM: 800000, BATCH_QUEUE_LIMIT: 80000000 }, - 'gpt-4': { RPM: 10000, TPM: 300000, BATCH_QUEUE_LIMIT: 30000000 }, - 'gpt-3.5-turbo': { RPM: 10000, TPM: 1000000, BATCH_QUEUE_LIMIT: 100000000 }, - 'text-embedding-3-large': { RPM: 10000, TPM: 5000000, BATCH_QUEUE_LIMIT: null }, - 'whisper-1': { RPM: 100, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1': { RPM: 100, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1-hd': { RPM: 10, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-2': { RPM: '100 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-3': { RPM: '15 img/min', TPM: null, BATCH_QUEUE_LIMIT: null } - }}, - tier5: {name:'Tier 5', price:50.00, description:'Paid tier with access to all models and additional features', limits:{ - 'gpt-4-turbo': { RPM: 10000, TPM: 1500000, BATCH_QUEUE_LIMIT: 250000000 }, - 'gpt-4': { RPM: 10000, TPM: 300000, BATCH_QUEUE_LIMIT: 45000000 }, - 'gpt-3.5-turbo': { RPM: 10000, TPM: 2000000, BATCH_QUEUE_LIMIT: 300000000 }, - 'text-embedding-3-large': { RPM: 10000, TPM: 10000000, BATCH_QUEUE_LIMIT: null }, - 'whisper-1': { RPM: 500, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1': { RPM: 500, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'tts-1-hd': { RPM: 20, TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-2': { RPM: '500 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, - 'dall-e-3': { RPM: '50 img/min', TPM: null, BATCH_QUEUE_LIMIT: null } - }} -} - - -module.exports = {models, tiers }; \ No newline at end of file diff --git a/OpenAI/openai-tokens-price.js b/OpenAI/openai-tokens-price.js deleted file mode 100644 index a905363..0000000 --- a/OpenAI/openai-tokens-price.js +++ /dev/null @@ -1,194 +0,0 @@ -const { get_encoding, encoding_for_model } = require ("tiktoken"); -const { Tiktoken } = require("tiktoken/lite"); -const cl100k_base = require("tiktoken/encoders/cl100k_base.json"); -const fromExponential = require('from-exponential'); -// const { Readable } = require ("stream"); -const { parseFile } = require ('music-metadata'); -const openaiData = require('./openai-data'); -const ut = require(process.cwd() + '/utilities'); -const math = ut.math; - -const tokens = { - count: ({text, model = "gpt-4-turbo-2024-04-09"}) => { - let enc; - try { - enc = encoding_for_model(model); - } - catch (error) { - enc = new Tiktoken( - cl100k_base.bpe_ranks, - cl100k_base.special_tokens, - cl100k_base.pat_str - ); - } - const tokens = enc.encode(text); - enc.free(); - return tokens.length; - } - - } - -const price = { - - estimate: async ({purpose, model, text = '', pathToFile = '', - options = {type: 'prompt', detail:'low', resolution:"1024x1024", quality:'standard'}}) => { - - const res = {price:0, tokens:0, options} - - let token_cost = 0, metadata; - if (text.length) res.tokens = tokens.count({text, model}); - - - const usedModel = openaiData.models[purpose][model]; - const prices = {} - for (let key in usedModel.prices){ - if (['training', 'prompt', 'completion'].includes(key)) prices[key] = math.divide(usedModel.prices[key], 1000000); - } - // console.log(usedModel.prices) - - switch (purpose){ - - case 'completeon': - token_cost = options.type == 'prompt' ? prices.prompt : prices.completion; - res.price = math.multiply(res.tokens, token_cost); - break; - case 'embedding': - token_cost = prices.prompt; - res.price = math.multiply(res.tokens, token_cost); - break; - case 'recognition': - res.tokens += usedModel.tokens.base; - if (options.detail !== 'low') { - metadata = await sharp(pathToFile).metadata(); - const scaled = price._scaleAndMeasureImage(metadata.width, metadata.height); - // console.log(metadata.width, metadata.height, scaled); - res.tokens += scaled.numberOfSquares * usedModel.tokens.block; - } - - res.price = math.multiply(res.tokens, prices.prompt); - break; - - case 'images': - res.price = usedModel.prices[options.quality][options.resolution]; - break; - - case 'fineTune': - if(options.type === 'training'){ - res.tokens = await files.countFileTokens({pathToFile, model}); - res.price = math.multiply(res.tokens, prices.training); - } - else { - token_cost = options.type == 'prompt' ? prices.prompt : prices.completion; - res.price = math.multiply(res.tokens, token_cost); - } - break - - case 'speech': - if (model === 'whisper-1'){ - metadata = await parseFile(pathToFile); - res.duration = Math.round(metadata.format.duration); - res.price = math.multiply(res.duration, usedModel.prices.minute/60); - // console.log(inspect(metadata, { showHidden: false, depth: null })); - } - else { - res.price = math.multiply(res.tokens, prices.prompt); - } - - break; - - } - // res.string = res.price.toFixed(20).replace(/[0]+$/, ''); - res.string = fromExponential(res.price) - return res; - }, - - calculateByUsage: ({purpose, model, usage, usages}) => { - const usedModel = openaiData.models[purpose][model]; - const prices = {} - for (let key in usedModel.prices){ - if (['training', 'prompt', 'completion'].includes(key)) prices[key] = math.divide(usedModel.prices[key], 1000000); - } - - let tokens = {prompt_tokens:0, completion_tokens:0, total_tokens:0}, total = 0, prompt = 0, completion = 0, res = {}; - - if (usage){ - const {prompt_tokens, completion_tokens} = usage; - prompt = math.multiply(prompt_tokens, prices.prompt); - completion = math.multiply(completion_tokens, prices.completion); - total = math.add(prompt, completion); - res = {...usage}; - } - else if (usages){ - for (let usage of usages){ - const {prompt_tokens, completion_tokens} = usage; - tokens.prompt_tokens += prompt_tokens; - tokens.completion_tokens += completion_tokens; - tokens.total_tokens += math.add(prompt_tokens, completion_tokens); - prompt += math.multiply(prompt_tokens, prices.prompt); - completion += math.multiply(completion_tokens, prices.completion); - } - total = math.add(prompt, completion); - res = tokens; - } - res.prompt_price = prompt; - res.completion_price = completion; - res.total_price = total; - // res.prices = { prompt, completion, total }; - // res.strings = { prompt:fromExponential(prompt), completion:fromExponential(completion), total:fromExponential(total) }; - return res; - }, - - - - _scaleAndMeasureImage(width, height) { - // Define the constraints - const maxLongSide = 2048; - const maxShortSide = 768; - const squareSize = 512; - - // Step 1: Scale down if the longest side is more than 2048 pixels - let aspectRatio = width / height; - if (width > height) { - if (width > maxLongSide) { - width = maxLongSide; - height = width / aspectRatio; - } - } else { - if (height > maxLongSide) { - height = maxLongSide; - width = height * aspectRatio; - } - } - - // Step 2: Scale down further if the shortest side is more than 768 pixels after step 1 - if (Math.min(width, height) > maxShortSide) { - if (width < height) { - width = maxShortSide; - height = width / aspectRatio; - } else { - height = maxShortSide; - width = height * aspectRatio; - } - } - - // Make sure the final dimensions are rounded to avoid fractional pixels - width = Math.round(width); - height = Math.round(height); - - // Step 3: Calculate the number of squares needed - const squaresWidth = Math.ceil(width / squareSize); - const squaresHeight = Math.ceil(height / squareSize); - const totalSquares = squaresWidth * squaresHeight; - - return { - scaledWidth: width, - scaledHeight: height, - numberOfSquares: totalSquares - }; - } - - } - - - - module.exports = {price, tokens} \ No newline at end of file diff --git a/OpenAI/tests/openai-connector.test.js b/OpenAI/tests/openai-connector.test.js deleted file mode 100644 index e92ea9b..0000000 --- a/OpenAI/tests/openai-connector.test.js +++ /dev/null @@ -1,369 +0,0 @@ -const fs = require ("fs"); -const { - //TEXT - - Chat, //class Chat - // chat.message({text}) => {message, messages, usages} => string - // chat.voiceMessage({inputFilePath, outputFilePath, voice}) => {inputText, outputText, outputFilePath} - tokens, - // tokens.count({text, model}) => integer - language, - // language.generate({text, messages, system, model, tools, tool_choice}) => {message, messages, usages} - // language.embedding({text, model}) => {embedding, usage} - // language.classification({text, model}) => responce - files, - // files.create({pathToFile, purpose}) => responce - // files.list() => data (array) - // files.retrieve({file_id}) => responce - // files.content({file_id}) => responce - // files.del({file_id}) => responce - fineTune, - // fineTune.create({pathToFile, training_file, hyperparameters, suffix, model, deleteFile, maxTokens}) => responce - // fineTune.list() => data (array) - // fineTune.events({id, limit}) => responce - does not work - // fineTune.retrieve({id}) => responce - // fineTune.cancel{({id}) => responce - models, - // models.list() => responce - // models.retrieve({model_id}) => responce - // models.del({model_id}) => responce - images, - // images.create({text, pathToFile, size, quality, n, model}) => {url, local} - // images.edit({text, pathToFile, pathToMask, size, n, model}) => responce - // images.variation({pathToFile, size, n, model}) => responce - speech, - // speech.textToSpeech({text, pathToFile, voice, model}) => buffer - // speech.speechToText({pathToFile, model}) => string - // speech.speechTranslation({pathToFile, model}) => string // does not translate - recognition, - // recognition.image({url, pathToFile, prompt, max_tokens, model}) => {message, usage} - // recognition.video({pathToFile, outputDir, max_tokens, model}) => {message, usage} - assistants //test - // assistants.create({name, instructions, tools, model}) => responce - // assistants.list() => data (array) - // assistants.retrieve({assistant_id}) => responce - // assistants.update({assistant_id, name, instructions, tools, model, file_ids}) => responce - // assistants.del({assistant_id}) => responce - - // assistants.files.create({file_id}) => responce - // assistants.files.list({assistant_id}) => data (array) - // assistants.files.retrieve({assistant_id, file_id}) => responce - // assistants.files.del({assistant_id, file_id}) => responce - - // assistants.threads.create({messages = []}) => responce - // assistants.threads.createAndRun({assistant_id, thread = {messages:[]}}) => responce - // assistants.threads.retrieve({thread_id}) => responce - // assistants.threads.update({thread_id, messages}) => responce - // assistants.threads.del({thread_id}) => responce - - // assistants.threads.messages.create({thread_id, role, content}) => responce - // assistants.threads.messages.list({thread_id, clean}) => data(array) - // assistants.threads.messages.listFiles({thread_id, message_id}) => data (array) //does not work - // assistants.threads.messages.retrieve({thread_id, message_id}) => responce - // assistants.threads.messages.retrieveFile({thread_id, message_id, file_id}) => responce - // assistants.threads.messages.update({thread_id, message_id, user_id}) => responce - - // assistants.threads.runs.create({thread_id, assistant_id}) => responce - // assistants.threads.runs.list({thread_id}) => responce - // assistants.threads.runs.retrieve({thread_id, run_id}) => responce - // assistants.threads.runs.listRunSteps({thread_id, run_id, limit, clean}) => responce - // assistants.threads.runs.retrieveRunStep({thread_id, run_id, step_id}) => responce - // assistants.threads.runs.update({thread_id, run_id, data}) => responce - // assistants.threads.runs.submitToolOutput({thread_id, run_id, output}) => responce - // assistants.threads.runs.cancel({thread_id, run_id}) => responce - -} - = require('../openai-connector'); - - - -// Default timeout -jest.setTimeout(50000); -const chat = new Chat({}); -const tested = { - chat: new Chat({}), - fineTune:{ - id:'', - file:{id:''} - }, - model:{ - id:'' - }, - assistant:{ - id: '', - thread: {id:''}, - message:{id:''}, - run:{id:''}, - step:{id:''}, - file:{id:''}, - added_file:{id:''} - } -} -const clean = async () => { - -} -let file_id = 'file-X4ZR3WmEyxJKOEuyuthIIj9T'; -let ftJobId = 'ftjob-WUootDAvgqK1iJlpTn1XZmoO'; -let modelId = 'gpt-3.5-turbo-16k'; -// -describe('Openai Connector', () => { - - describe('Openai Chat', () => { - - test('Chat message', async () => { - const res = await chat.message({text: "Hello"}); - expect(typeof res).toBe('string'); - }); - - - test('Chat voice message', async () => { - const res = await chat.voiceMessage({ - inputFilePath:'./OpenAI/tests/speech/test-speech-input-en.mp3', - outputFilePath:'./OpenAI/tests/speech/test-speech-output-en.mp3', - returnIntermediateResult:false}); - - expect(res).toHaveProperty('inputText'); - expect(res.inputText).toBe('Hello there.'); - expect(res).toHaveProperty('outputText'); - expect(res).toHaveProperty('outputFilePath'); - }); - - }) - - describe('Openai Language', () => { - - test('Text completion', async () => { - - const res = await language.generate({text:'hello'}); - - expect(res).toHaveProperty('message'); - expect(res).toHaveProperty('messages'); - expect(res).toHaveProperty('usage'); - }); - - test('Text completeon with function call', async () => { - const testLibrary = require('./tools/test-library'); - const res = await language.generate({text: "What's the weather like in San Francisco, Tokyo, and Paris?", tools:testLibrary.tools}); - - expect(res).toHaveProperty('message'); - expect(res).toHaveProperty('messages'); - expect(res).toHaveProperty('usages'); - }); - - test('Text Embedding', async () => { - - res = await language.embedding({text:'hello'}); - - expect(res).toHaveProperty('embedding'); - expect(res).toHaveProperty('usage'); - }); - - test('Text Classification', async () => { - const res = await language.classification({text:'I will kill you boatman'}); - expect(res.flagged).toBe(true); - expect(res.categories.violence).toBe(true); - expect(res.category_scores).toHaveProperty('violence'); - }); - }) - - describe('Openai Files', () => { - - test('Count file tokens', async () => { - const res = await files.countFileTokens({pathToFile:'./OpenAI/tests/fine-tune/test-fine-tune-24.jsonl'}); - // const res = await files.create({pathToFile:'./OpenAI/tests/fine-tune/test-fine-tune-24.jsonl', purpose:'fine-tune'}); - expect(res).toBe(1889); - }); - - test('Creare fine-tune file', async () => { - const res = await files.create({pathToFile:'./OpenAI/tests/fine-tune/test-fine-tune-24.jsonl', purpose:'fine-tune'}); - expect(res).toHaveProperty('id'); - expect(res.status).toBe('processed'); - tested.fineTune.file.id = res.id - }); - test('Creare assistant file', async () => { - const res = await files.create({pathToFile:'./OpenAI/tests/assistants/test.csv', purpose:'assistants'});; - expect(res).toHaveProperty('id'); - expect(res.status).toBe('processed'); - tested.assistant.file.id = res.id; - }); - - test('List files', async () => { - const res = await files.list(); - expect(Array.isArray(res)).toBe(true); - }); - - test('Retrieve file', async () => { - // let file_id = tested.fineTune.file.id; - - const res = await files.retrieve({file_id}); - expect(res).toHaveProperty('id'); - expect(res.id).toBe(file_id); - }); - - test('Retrieve file content', async () => { - // let file_id = tested.fineTune.file.id; - - const res = await files.content({file_id}); - expect(typeof res).toBe('string'); - }); - - test('Delete file', async () => { - // let file_id = tested.fineTune.file.id; - - const res = await files.del({file_id}); - expect(res).toHaveProperty('id'); - expect(res.id).toBe(file_id); - expect(res.deleted).toBe(true); - }); - }); - - describe('Openai Fine Tune', () => { - - test('Create Fine Tune job', async () => { - // let file_id = tested.fineTune.file.id; - - const res = await fineTune.create({pathToFile:'./OpenAI/tests/fine-tune/test-fine-tune-24.jsonl'}) - expect(res).toHaveProperty('id'); - expect(res.error.error).toBe(null); - tested.fineTune.id = res.id; - tested.fineTune.file.id = res.training_file; - }); - test('Create Fine Tune from training file', async () => { - // let file_id = tested.fineTune.file.id; - const res = await fineTune.create({training_file:file_id}) - expect(res).toHaveProperty('id'); - expect(res.error.error).toBe(null); - // tested.fineTune.id = res.id; - }); - // Does not work - // test('Get Fine Tune events', async () => { - // // let ftJobId = tested.fineTune.id; - // const res = await fineTune.events({id:ftJobId}) - // }); - test('List Fine Tune jobs', async () => { - const res = await fineTune.list(); - expect(Array.isArray(res)).toBe(true); - }); - test('Retrieve Fine Tune job', async () => { - // let ftJobId = tested.fineTune.id; - const res = await fineTune.retrieve({id:ftJobId}); - expect(res).toHaveProperty('id'); - expect(res.id).toBe(ftJobId); - }); - // Require to catch error if job is completed - // test('Cancel Fine Tune job', async () => { - // // let ftJobId = tested.fineTune.id; - // const fn = async () => await fineTune.cancel({id:ftJobId}); - // expect(fn).toThrow(TypeError); - - // }); - }); - - describe('Openai Models', () => { - test('List models', async () => { - const res = await models.list(); - expect(Array.isArray(res)).toBe(true); - const custom_model = res.find(model => model.id.startsWith('ft')); - tested.model.id = custom_model.id; - }); - test('Retrieve model', async () => { - const res = await models.retrieve({model_id:modelId}); - expect(res).toHaveProperty('id'); - expect(res.id).toBe(modelId); - }); - // test('Delete model', async () => { - // const res = await models.del({model_id:modelId}); - // expect(res).toHaveProperty('deleted'); - // expect(res.deleted).toBe(true); - // }); - }); - - describe('Openai Audio', () => { - test('textToSpeech', async () => { - const pathToFile = './OpenAI/tests/speech/test-speech-output-en.mp3'; - await fs.promises.unlink(pathToFile).catch(err => console.error(err)); - const res = await speech.textToSpeech({text:'Hello, how can I help you?', pathToFile}); - const stat = await fs.promises.stat(pathToFile); - expect(Buffer.isBuffer(res)).toBe(true); - expect(stat).toHaveProperty('uid'); - }); - test('speechToText', async () => { - const pathToFile = './OpenAI/tests/speech/test-speech-input-en.mp3'; - const res = await speech.speechToText({pathToFile}); - expect(typeof res).toBe('string'); - }); - test('Speech Translation', async () => { - const pathToFile = './OpenAI/tests/speech/test-speech-input-ru.mp3'; - const res = await speech.speechTranslation({pathToFile}); - expect(typeof res).toBe('string'); - }); - }) - - describe('Openai Images', () => { - test('Image Generation', async () => { - const saveAs = './OpenAI/tests/images/test-image-create-result.jpg'; - // try { - // await fs.promises.unlink(pathToFile) - // } catch (error) {} - - const res = await images.create({text:"a white siamese cat", saveAs}); - // const stat = await fs.promises.stat(pathToFile); - expect(res).toHaveProperty('url'); - expect(res).toHaveProperty('local'); - // expect(stat).toHaveProperty('uid'); - }); - - test('Image Edit', async () => { - const saveAs = './OpenAI/tests/images/test-edit-image-result.jpg'; - // try { - // await fs.promises.unlink(saveAs) - // } catch (error) {} - - const res = await images.edit({text:"A futuristic landscape behind a foregraund emoticon", - pathToFile:'./OpenAI/tests/images/test-edit-image.png', - pathToMask: './OpenAI/tests/images/test-edit-image.png', - saveAs, - size: "256x256"}); - // const stat = await fs.promises.stat(saveAs); - expect(res).toHaveProperty('url'); - expect(res).toHaveProperty('local'); - // expect(stat).toHaveProperty('uid'); - }); - - test('Image Variation', async () => { - const pathToFile = './OpenAI/tests/images/test-edit-image.png'; - const saveAs = './OpenAI/tests/images/test-image-variation-result.jpg'; - // try { - // await fs.promises.unlink(saveAs) - // } catch (error) {} - - const res = await images.variation({pathToFile, saveAs}); - // const stat = await fs.promises.stat(saveAs); - expect(res).toHaveProperty('url'); - expect(res).toHaveProperty('local'); - // expect(stat).toHaveProperty('uid'); - }); - }); - describe('Openai Recognition', () => { - test('Image Recognition', async () => { - const pathToFile = './OpenAI/tests/images/test-image.jpg'; - const res = await recognition.image({pathToFile}); - expect(res).toHaveProperty('message'); - expect(res).toHaveProperty('usage'); - }); - - // .....Too heavy for testing - // test('Video Recognition', async () => { - // const pathToFile = './OpenAI/tests/videos/cat-no.mp4'; - // const outputDir = './OpenAI/tests/videos/video-frames' - // const res = await recognition.video({pathToFile, outputDir, frameRate: 5}); - // expect(res).toHaveProperty('message'); - // expect(res).toHaveProperty('usage'); - // }); - - }) - - - - -}) diff --git a/OpenAI/tests/speech/test-speech-output-en.mp3 b/OpenAI/tests/speech/test-speech-output-en.mp3 deleted file mode 100644 index a526e6c3f02fccc363a2b513b4d31702acdfecc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38880 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 diff --git a/OpenAI/tests/tools/test-library.js b/OpenAI/tests/tools/test-library.js deleted file mode 100644 index 0da34d4..0000000 --- a/OpenAI/tests/tools/test-library.js +++ /dev/null @@ -1,55 +0,0 @@ -const getCurrentWeather = ({location, unit = "fahrenheit"}) => { - if (location.toLowerCase().includes("tokyo")) { - return JSON.stringify({ location: "Tokyo", temperature: "10", unit: "celsius" }); - } else if (location.toLowerCase().includes("san francisco")) { - return JSON.stringify({ location: "San Francisco", temperature: "72", unit: "fahrenheit" }); - } else if (location.toLowerCase().includes("paris")) { - return JSON.stringify({ location: "Paris", temperature: "22", unit: "fahrenheit" }); - } else { - return JSON.stringify({ location, temperature: "unknown" }); - } - } - const getCurrentAge = ({location, unit = "years"}) => { - if (location.toLowerCase().includes("tokyo")) { - return JSON.stringify({ location: "Tokyo", age: "10", unit: "years" }); - } else if (location.toLowerCase().includes("san francisco")) { - return JSON.stringify({ location: "San Francisco", age: "72", unit: "years" }); - } else if (location.toLowerCase().includes("paris")) { - return JSON.stringify({ location: "Paris", age: "22", unit: "years" }); - } else { - return JSON.stringify({ location, temperature: "unknown" }); - } - } - - const tools = [{ - // name: "get_current_weather", - - fn: getCurrentWeather, - scope: this, - description: "Get the current weather in a given location", - properties: { - location: { - type: "string", - description: "The city and state, e.g. San Francisco, CA", - }, - unit: { type: "string", enum: ["celsius", "fahrenheit"] }, - }, - required: ["location"] - }, - { - // name: "get_current_weather", - - fn: getCurrentAge, - scope: this, - description: "Get the current population average age in a given location", - properties: { - location: { - type: "string", - description: "The city and state, e.g. San Francisco, CA", - }, - unit: { type: "string", enum: ["years", "days"] }, - }, - required: ["location"] - }]; - -module.exports = {getCurrentWeather, getCurrentAge, tools} \ No newline at end of file diff --git a/README.md b/README.md index 71e6891..e8a6e78 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,79 @@ -# Universal LLMs +# Universal LLM This module should manage interactions with AI language models. For starters: Openai, Huggingface, Ollama and Elevenlabs. -## Requrements -Create separate local postgresql database with optional [pgvector](https://github.com/pgvector/pgvector) extension (last version postgresql - 16). - -I recommend to install [timescaledb](https://neon.tech/docs/extensions/timescaledb) as well, but you can skip it for now. +## Requirements Install [ffmpeg](https://ffmpeg.org/) for audio and video processing. - - -Run npm install - -Create in the root directory .env file with the following content: +```bash +npm install ``` -PGUSER='' -PGPASSWORD='' -PGDATABASE='' -PGHOST='' -PGPORT='' - - - -MJ_APIKEY_PUBLIC='your API key' -MJ_APIKEY_PRIVATE='your API secret' -MJ_API_TOKEN='your API token' - - -OPENAI_API_KEY='' -HUGGINGFACE_TOKEN='' -ELEVENLABS_API_KEY='' -DOMAIN='localhost' -ADMIN_EMAIL='your_email' -TEST_USER_EMAIL='your_email' - -``` - -Fill the .env file with the following data: -* Your local postgres credentials - -* Register free openai account and get the API key (for developers of LLM module) -* Register free huggingface account and get the token (for developers of LLM module) -* Register free elevenlabs account and get the API key (for developers using Elevenlabs speech-to-text module) - -Check periodically changes of the package.json file and install the new dependencies. - -All non private global settings, like port numbers etc. you should store in config.js - -## Modules - - - - - -### DB - -This module should manage interactions with databases. For starters: Postgres. -You should install [pgvector](https://github.com/pgvector/pgvector) extension for embeddings. - -Next: Redis, Mongo, Cassandra and [Singlestore](https://www.singlestore.com/). ### LLM - - -**Openai** - there is the connector class managing many kinds of interactions with the Openai API, mostly self explanatory. +**Openai** - there is the connector class managing many kinds of interactions with the Openai API, mostly self explanatory. I will add image generations in the nearest future. You should have an openai account and the [API key](https://platform.openai.com/api-keys) in .env file. **Huggingface** - there is the connector class managing many kinds of interactions with the Huggingface API to multiple open-source LLMs, mostly self explanatory. -You should obtain [Hugginface token](https://huggingface.co/settings/tokens) and place it in .env file. +You should obtain [Hugginface token](https://huggingface.co/settings/tokens) and place it in .env file. To compare manually performance and quality of open source models in parallel you can use [lmsys.org](https://chat.lmsys.org/). -**Ollama** - there is the connector class managing many kinds of interactions with multiple local LLMs. +**Ollama** - there is the connector class managing many kinds of interactions with multiple local LLMs. You should install [Ollama](https://github.com/ollama/ollama) on your local machine to use this module. Then run following commands (with your preferred model - in this case llama2): -``` +```bash ollama serve ollama run llama2 -``` +``` **Elevenlabs** - one of the most popular speech-to-text services. There is the connector class managing many kinds of interactions with the Elevenlabs API, mostly self explanatory. You should obtain [Elevenlabs API key](https://www.eleven-labs.com/en/docs/speech-to-text/getting-started) and place it in .env file. +## Testing +Running for all tests: +```bash +npm t +``` -## Testing +Running for single test file with environments file: -I have an experience with Jest and I would like to use it for testing (but we may set other systems in parrallel). -To check a module functionality without Jest you can add some code to index.js in the root, but keep your changes locally and don't send them to github. -Check the example of Jest usage in OpenAi/tests/openai-connectors.test.js. -Run tests with: npm test command for all tests (though it's not recommended) or run particular test with following code: +```bash +node --env-file=.env --test 'test/openai.js' ``` -// pattern: -jest sample-file -// OR file name: -jest path/to/sample-file.test.js -``` -If you use VS code, don't forget to set "jest.runMode": "on-demand" in the settings.json file, to prevent automatic API calls. +Running for single test in a test file: -ALternatively, you can check manually if a method works by placing following code in the index.js file in the root (in this case we testing Elevenlabs module): +```bash +node --env-file=.env --test --test-name-pattern="/Text completion/" 'test/openai.js' ``` -(async () => { - const res = await require('./Elevenlabs/elevenlabs-connector').getVoice(); -})() +## Usage + +**OpenAi Example** + +```javascript +'use strict'; + +const { openai } = require('./lib'); + +const { Chat } = openai; + +const chat = new Chat({ apiKey: process.env.OPENAI_API_KEY }); + +(async () => { + const msg = await chat.message({ text: 'Hello!' }); + console.log({ msg }); +})(); ``` -Use ut.callAPI universal method to call API endpoints in modules. + +Use common.callAPI universal method to call API endpoints in modules. If LOG_API_CALL_TIME config variable is set to true, each method should measure and print the each API call time. If LOG_API_CALL_RESULT config variable is set to true, each method should print the full api responce to pick required portion of data to return from a method. -For those who unfamiliar with backend, you run following command in the terminal: -``` -node index.js -``` -Just don't forget to remove the code from the index.js file after testing. - ## Diagnostics I would like to use [Clinic.js](https://clinicjs.org/) for diagnostics, but we may set other systems in parrallel. @@ -139,25 +94,22 @@ A user should manually peak and install the module dependencies from the package ## Contributing -Anyone is welcome to contribute to the project. The project is open for the public and I would like to keep it that way. +Anyone is welcome to contribute to the project. The project is open for the public and I would like to keep it that way. Peak a module you see a potential in and start working on it. ## TODO list - -* Write tests for openaAI assisitants -* Finish Huggingface module and write JEST tests -* Add price calculation for all methods that return usage statistics -* Add price estimation function for all methods, that do not return usage statistics -* Add "my_" prefix to all generated dusting tests files, so git will ignore them -* Make frontend allowing to interact with multiple LLMs in parralel and compare output -* Connect the frontend to openai, huggingface and ollama modules -* Make frontend to visualise dialogues and all other interactions with LLMs -* Allow dinamic API_KEY definition (stored on frontend side) - - - - -## +- [x] Make LLM to be separate subrepository, with all dependencies and tests. +- [ ] Write tests for openaAI assisitants +- [ ] Finish Huggingface module and write tests +- [ ] Add price calculation for all methods that return usage statistics +- [ ] Add price estimation function for all methods, that do not return usage statistics +- [x] Add "my\_" prefix to all generated dusting tests files, so git will ignore them +- [ ] Make frontend allowing to interact with multiple LLMs in parralel and compare output +- [ ] Connect the frontend to openai, huggingface and ollama modules +- [ ] Make frontend to visualise dialogues and all other interactions with LLMs +- [x] Allow dynamic API_KEY definition (stored on frontend side) + +## To be continued... diff --git a/config.js b/config.js deleted file mode 100644 index 68ea7cb..0000000 --- a/config.js +++ /dev/null @@ -1,8 +0,0 @@ - - -module.exports = { - LOG_API_CALL_TIME: true, - LOG_API_CALL_RESULT: false, - LOG_API_CALL_ERRORS: true, - OLLAMA_PORT:11434, -} \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..5d9efa3 --- /dev/null +++ b/config.json @@ -0,0 +1,12 @@ +{ + "callApiOpts": { + "logTime": true, + "logResult": false, + "logErrors": true + }, + "ollamaOpts": { + "transport": "http", + "host": "localhost", + "port": 11434 + } +} diff --git a/public/js/main.js b/files/elevenlabs/.gitkeep similarity index 100% rename from public/js/main.js rename to files/elevenlabs/.gitkeep diff --git a/files/elevenlabs/audio.mp3 b/files/elevenlabs/audio.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..71fe4808c1c5a1c41bfd8298a67c707613a3db3e GIT binary patch literal 10866 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%0A* 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 { + const targetLocation = location.toLowerCase(); + for (const expectedLocation of locations) { + const { location } = expectedLocation; + if (location.toLowerCase() === targetLocation) { + const result = { ...expectedLocation }; + delete result.age; + return JSON.stringify(result); + } + } + return JSON.stringify({ + location, + unit: 'unknown', + temperature: 'unknown', + }); +}; + +const getCurrentAge = ({ location /*unit = 'years'*/ }) => { + const targetLocation = location.toLowerCase(); + for (const expectedLocation of locations) { + const { location } = expectedLocation; + if (location.toLowerCase() === targetLocation) { + const result = { ...expectedLocation }; + delete result.temperature; + return JSON.stringify(result); + } + } + return JSON.stringify({ + location, + unit: 'unknown', + age: 'unknown', + }); +}; + +const tools = [ + { + // name: "get_current_weather", + + fn: getCurrentWeather, + scope: this, + description: 'Get the current weather in a given location', + properties: { + location: { + type: 'string', + description: 'The city and state, e.g. San Francisco, CA', + }, + unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }, + }, + required: ['location'], + }, + { + // name: "get_current_weather", + + fn: getCurrentAge, + scope: this, + description: 'Get the current population average age in a given location', + properties: { + location: { + type: 'string', + description: 'The city and state, e.g. San Francisco, CA', + }, + unit: { type: 'string', enum: ['years', 'days'] }, + }, + required: ['location'], + }, +]; + +module.exports = { + getCurrentWeather, + getCurrentAge, + tools, +}; diff --git a/OpenAI/tests/videos/cat-no.mp4 b/files/openai/videos/cat-no.mp4 similarity index 100% rename from OpenAI/tests/videos/cat-no.mp4 rename to files/openai/videos/cat-no.mp4 diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 02bed95..0000000 --- a/jest.config.js +++ /dev/null @@ -1,200 +0,0 @@ -/** - * For a detailed explanation regarding each configuration property, visit: - * https://jestjs.io/docs/configuration - */ - -/** @type {import('jest').Config} */ -const config = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/dy/yqh43f0d407_lv2_4px_9c700000gn/T/jest_dx", - - // Automatically clear mock calls, instances, contexts and results before every test - // clearMocks: false, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: undefined, - - // The directory where Jest should output its coverage files - // coverageDirectory: undefined, - - // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], - - // Indicates which provider should be used to instrument code for coverage - coverageProvider: "v8", - - // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // "json", - // "text", - // "lcov", - // "clover" - // ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: undefined, - - // A path to a custom dependency extractor - // dependencyExtractor: undefined, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // The default configuration for fake timers - // fakeTimers: { - // "enableGlobally": false - // }, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: undefined, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: undefined, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. - // maxWorkers: "50%", - - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], - - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "mjs", - // "cjs", - // "jsx", - // "ts", - // "tsx", - // "json", - // "node" - // ], - - // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module - // moduleNameMapper: {}, - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: undefined, - - // Run tests from one or more projects - // projects: undefined, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state before every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: undefined, - - // Automatically restore mock state and implementation before every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: undefined, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // The number of seconds after which a test is considered as slow and reported as such in the results. - // slowTestThreshold: 5, - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - // testEnvironment: "jest-environment-node", - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - // testMatch: [ - // "**/__tests__/**/*.[jt]s?(x)", - // "**/?(*.)+(spec|test).[tj]s?(x)" - // ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: undefined, - - // This option allows use of a custom test runner - // testRunner: "jest-circus/runner", - - // A map from regular expressions to paths to transformers - // transform: undefined, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/", - // "\\.pnp\\.[^\\/]+$" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: undefined, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, - - setupFilesAfterEnv: ["jest-extended/all"] -}; - -module.exports = config; diff --git a/lib/common.js b/lib/common.js new file mode 100644 index 0000000..af5af38 --- /dev/null +++ b/lib/common.js @@ -0,0 +1,230 @@ +'use strict'; + +const fs = require('node:fs'); +const fsp = fs.promises; +const path = require('node:path'); +const { once } = require('node:events'); +const readline = require('node:readline'); +const { Readable } = require('node:stream'); +const ffmpeg = require('fluent-ffmpeg'); +const { callApiOpts } = require('../config.json'); + +const SEC_ON_MS = 1_000; +const MIN_ON_SEC = 60; + +const compose = (...fns) => { + const reducer = (x) => fns.reduceRight((res, f) => f(res), x); + return reducer; +}; + +const measureTime = (begin) => { + if (!begin) return performance.now(); + return Math.floor(performance.now() - begin); +}; + +const formatTime = (time) => { + const seconds = Math.floor(time / SEC_ON_MS); + if (seconds < MIN_ON_SEC) { + return `${seconds}sec ${time - seconds * SEC_ON_MS}ms`; + } + const minutes = Math.floor(seconds / MIN_ON_SEC); + const remainingSeconds = seconds % MIN_ON_SEC; + const milliseconds = Math.floor((time - seconds * MIN_ON_SEC) / 10); + return `${minutes}min ${remainingSeconds}sec ${milliseconds}ms`; +}; + +const formatMeasureTime = compose(formatTime, measureTime); + +const callAPI = async (library, methodPath, ...args) => { + const { logTime, logResult, logError } = callApiOpts; + const methodName = methodPath.split('.').at(-1); + const method = library[methodName]; + + if (!method || typeof method !== 'function') { + const message = `Method ${methodPath} not found`; + return { error: new Error(message) }; + } + + const startTime = logTime ? measureTime() : 0; + + try { + const result = await method.call(library, ...args); + if (startTime !== 0) { + const endTime = formatMeasureTime(startTime); + console.log({ [methodPath]: endTime }); + } + if (logResult) console.log(`${methodPath} result:`, result); + return result; + } catch (error) { + const message = `Error occured while calling ${methodPath}`; + if (logError) console.error(message, error); + return { error }; + } +}; + +const delay = (msec) => new Promise((r) => void setTimeout(r, msec)); + +const random = (min, max) => Math.round(Math.random() * (max - min) + min); + +const typeSizes = { + undefined: () => 0, + boolean: () => 4, + number: () => 8, + bigint: () => 8, + string: (item) => 2 * item.length, + object: (item = null, calcSize) => { + if (!item) return 0; + const totalSize = Object.keys(item).reduce((total, key) => { + const keySize = calcSize(key); + const valueSize = calcSize(item[key]); + return total + keySize + valueSize; + }, 0); + return totalSize; + }, +}; + +const roughSizeOfObject = (value) => { + const type = typeof value; + const method = typeSizes[type]; + return method(value, roughSizeOfObject); +}; + +const formatBytes = (bytes, decimals = 2) => { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +}; + +const baseUnits = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; +const siUnits = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; + +const humanFileSize = (bytes, si = false, dp = 1) => { + const thresh = si ? 1000 : 1024; + if (Math.abs(bytes) < thresh) return bytes + ' B'; + + const units = si ? siUnits : baseUnits; + + let u = -1; + const r = 10 ** dp; + + do { + bytes /= thresh; + } while ( + Math.round(Math.abs(bytes) * r) / r >= thresh && + u++ < units.length - 1 + ); + + return bytes.toFixed(dp) + ' ' + units[u]; +}; + +const extractVideoFrames = async ( + videoPath, + outputDir, + frameRate = 1, + prefix = 'frame', +) => + new Promise((resolve, reject) => { + const options = [ + '-vf fps=' + frameRate, + '-q:v 15', + // '-update 1', + ]; + ffmpeg(videoPath) + .outputOptions(options) + .output(outputDir + `/${prefix}%04d.jpg`) + .on('end', resolve) + .on('error', reject) + .run(); + }); + +const cleanDirectory = async (dir) => { + try { + const files = await fsp.readdir(dir); + for (const file of files) { + const filePath = path.join(dir, file); + await fsp.unlink(filePath); + } + } catch (error) { + console.error('Error cleaning directory:', error); + } +}; + +const processFileLineByLine = async ( + filePath, + processLineCallback = null, + finishCallback = null, +) => { + let index = 0; + const startMemory = process.memoryUsage().heapUsed; + const startTime = measureTime(); + try { + const stat = await fsp.stat(filePath); + const rl = readline.createInterface({ + input: fs.createReadStream(filePath), + crlfDelay: Infinity, + }); + + rl.on('line', (line) => void processLineCallback({ line, index: index++ })); + await once(rl, 'close'); + + if (!finishCallback || typeof finishCallback !== 'function') return; + const size = humanFileSize(stat.size, true); + const currentMemory = process.memoryUsage().heapUsed; + const memoryUsed = currentMemory - startMemory; + const mb = Math.round((memoryUsed / 1024 / 1024) * 100) / 100 + ' MB'; + const endTime = formatTime(startTime); + const result = { filePath, size, lines: index, used: mb, time: endTime }; + finishCallback(result); + } catch (err) { + console.error(err); + } +}; + +const saveFileFromWeb = (url, path) => + new Promise((resolve) => { + const ws = fs.createWriteStream(path); + ws.on('finish', () => void resolve(Object.create(null))); + ws.on('error', (error) => void resolve({ error })); + fetch(url).then( + async (res) => { + const { ok, body, statusTest } = res; + if (ok) return void Readable.fromWeb(body).pipe(ws); + if (body) await body.cancel(); // https://undici.nodejs.org/#/?id=garbage-collection + const error = new Error(`unexpected response ${statusTest}`); + resolve({ error }); + }, + (error) => { + ws.on('close', () => void resolve({ error })); + ws.close(); + }, + ); + }); + +const fileIsExist = async (file) => + await fs.promises.access(file).then( + () => true, + () => false, + ); + +module.exports = { + callAPI, + measureTime, + delay, + random, + roughSizeOfObject, + formatBytes, + humanFileSize, + extractVideoFrames, + cleanDirectory, + processFileLineByLine, + saveFileFromWeb, + math: require('@dip1059/safe-math-js'), + formatMeasureTime, + fileIsExist, +}; diff --git a/lib/elevenlabs/connector.js b/lib/elevenlabs/connector.js new file mode 100644 index 0000000..d8fa52f --- /dev/null +++ b/lib/elevenlabs/connector.js @@ -0,0 +1,142 @@ +'use strict'; + +const ElevenLabs = require('elevenlabs-node'); +const { callAPI } = require('../common.js'); + +const { ELEVENLABS_API_KEY: apiKey = 'stubKey' } = process.env; + +const voice = new ElevenLabs({ + apiKey, // Your API key from Elevenlabs + // voiceId: "pNInz6obpgDQGcFmaJgB", + // A Voice ID from Elevenlabs (Adam) +}); + +/* + Required Parameters + fileName: "audio.mp3", // The name of your audio file + textInput: "mozzy is cool", // The text you wish to convert to speech + + Optional Parameters + voiceId: "21m00Tcm4TlvDq8ikWAM", // A different Voice ID from the default + stability: 0.5, // The stability for the converted speech + similarityBoost: 0.5, // The similarity boost for the converted speech + modelId: "eleven_multilingual_v2", // The ElevenLabs Model ID + style: 1, // The style exaggeration for the converted speech + speakerBoost: true // The speaker boost for the converted speech + + + fileName and file path for your audio file e.g (./gen/hello) String + textInput Text to be converted into audio e.g (Hello) String + stability Stability for Text to Speech default (0) Float + similarityBoost Similarity Boost for Text to Speech default (0) Float + voiceId ElevenLabs Voice ID e.g (pNInz6obpgDQGcFmaJgB) String + modelId ElevenLabs Model ID e.g (eleven_multilingual_v2) String + responseType Streaming response type e.g (stream) String + speakerBoost Speaker Boost for Text to Speech e.g (true) Boolean + style Style Exaggeration for Text to Speech (0-100) default (0) Integer +*/ + +const textToSpeech = async ( + textInput = 'Hello World', + fileName = process.cwd() + '/files/elevenlabs/audio.mp3', + voiceId = 'pNInz6obpgDQGcFmaJgB', +) => { + const result = await callAPI(voice, 'voice.textToSpeech', { + textInput, + fileName, + voiceId, + }); + return result; +}; +//{voiceId, textInput, stability, similarityBoost, +//modelId, responseType, style, speakerBoost} +const textToSpeechStream = async ( + voiceId, + textInput, + stability, + similarityBoost, + modelId, + responseType, + style, + speakerBoost, +) => { + await callAPI(voice, 'voice.textToSpeechStream', { + voiceId, + textInput, + stability, + similarityBoost, + modelId, + responseType, + style, + speakerBoost, + }); +}; + +const getVoices = async () => { + const result = await callAPI(voice, 'voice.getVoices'); + return result; +}; + +const getVoice = async (voiceId = 'pNInz6obpgDQGcFmaJgB') => { + const result = await callAPI(voice, 'voice.getVoice', { voiceId }); + return result; +}; + +const editVoiceSettings = async (voiceId, stability, similarityBoost) => { + const result = await callAPI(voice, 'voice.editVoiceSettings', { + voiceId, + stability, + similarityBoost, + }); + return result; +}; + +const getVoiceSettings = async (voiceId) => { + const result = await callAPI(voice, 'voice.getVoiceSettings', { voiceId }); + return result; +}; + +const deleteVoice = async (voiceId) => { + const result = await callAPI(voice, 'voice.deleteVoice', { voiceId }); + return result; +}; + +const getModels = async () => { + const result = await callAPI(voice, 'voice.getModels'); + return result; +}; + +const getUserInfo = async () => { + const result = await callAPI(voice, 'voice.getUserInfo'); + return result; +}; + +const getUserSubscription = async () => { + const result = await callAPI(voice, 'voice.getUserSubscription'); + return result; +}; + +const getDefaultVoiceSettings = async () => { + const result = await callAPI(voice, 'voice.getDefaultVoiceSettings'); + return result; +}; + +// async deleteVoice(voiceId = "pNInz6obpgDQGcFmaJgB") { +// const result = await voice.deleteVoice({ voiceId }); +// if (TEST) console.log(result); +// return result; +// } + +module.exports = { + textToSpeech, + textToSpeechStream, + getVoices, + getVoice, + editVoiceSettings, + getVoiceSettings, + deleteVoice, + getModels, + getUserInfo, + getUserSubscription, + getDefaultVoiceSettings, +}; diff --git a/lib/huggingface/connector.js b/lib/huggingface/connector.js new file mode 100644 index 0000000..451ec31 --- /dev/null +++ b/lib/huggingface/connector.js @@ -0,0 +1,478 @@ +'use strict'; + +const { HfInference } = require('@huggingface/inference'); +const { callAPI } = require('../common.js'); + +const HUGGINGFACE_TOKEN = process.env.HUGGINGFACE_TOKEN; + +const hf = new HfInference(HUGGINGFACE_TOKEN); + +// You can also omit "model" to use the recommended model for the task + +// constructor() { + +// } + +//......Natural Language Processing +// inputs = '[MASK] world!' + +const FillMask = async (inputs, model = 'bert-base-uncased') => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.fillMask', args); + return res; +}; + +/* + inputs = `The tower is 324 metres (1,063 ft) tall, about the same height + as an 81-storey building, and the tallest structure in Paris. + Its base is square, measuring 125 metres (410 ft) on each side. + During its construction, the Eiffel Tower surpassed the Washington + Monument to become the tallest`, + model = 'facebook/bart-large-cnn' +*/ + +const Summarization = async ( + inputs, + parameters = { max_length: 100 }, + model = 'facebook/bart-large-cnn', +) => { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.summarization', args); + return res; +}; + +/* + inputs = { + question: 'What is the capital of France?', + context: 'The capital of France is Paris.' + }, +*/ + +const QuestionAnswering = async ( + inputs, + model = 'deepset/roberta-base-squad2', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.questionAnswering', args); + return res; +}; + +/* + inputs = { + query: 'How many stars does the transformers repository have?', + table: { + Repository: ['Transformers', 'Datasets', 'Tokenizers'], + Stars: ['36542', '4512', '3934'], + Contributors: ['651', '77', '34'], + 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'] + } + }, +*/ + +const TableQuestionAnswering = async ( + inputs, + model = 'google/tapas-base-finetuned-wtq', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.tableQuestionAnswering', args); + return res; +}; + +// inputs = 'I like you. I love you.' + +const TextClassification = async ( + inputs, + model = 'distilbert-base-uncased-finetuned-sst-2-english', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.textClassification', args); + return res; +}; + +// inputs = 'The answer to the universe is' + +const TextGeneration = async (inputs, model = 'gpt2') => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.textGeneration', args); + return res; +}; +// inputs = 'repeat "one two three four"' +// parameters = { max_new_tokens: 250 } + +const TextGenerationStream = async ( + inputs, + parameters = {}, + model = 'google/flan-t5-xxl', +) => { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.textGenerationStream', args); + return res; +}; + +// inputs = 'My name is Sarah Jessica Parker but you can call me Jessica' + +const TokenClassification = async ( + inputs, + model = 'dbmdz/bert-large-cased-finetuned-conll03-english', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.tokenClassification', args); + return res; +}; + +// inputs = 'My name is Wolfgang and I live in Amsterdam', +// parameters = {"src_lang": "en_XX", "tgt_lang": "fr_XX"} + +const Translation = async (inputs, parameters = {}, model = 't5-base') => { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.translation', args); + return res; +}; +/* + inputs = [ + 'Hi, I recently bought a device from your company but it is not working' + + ' as advertised and I would like to get reimbursed!' + ], + parameters = { candidate_labels: ['refund', 'legal', 'faq'] } +*/ +const ZeroShotClassification = async ( + inputs, + parameters = {}, + model = 'facebook/bart-large-mnli', +) => { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.zeroShotClassification', args); + return res; +}; + +/* + inputs = { + source_sentence: 'That is a happy person', + sentences: [ + 'That is a happy dog', + 'That is a very happy person', + 'Today is a sunny day' + ] + } +*/ + +const SentenceSimilarity = async ( + inputs, + model = 'sentence-transformers/paraphrase-xlm-r-multilingual-v1', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.sentenceSimilarity', args); + return res; +}; + +//.........Audio +// data = readFileSync('test/sample1.flac') + +const AutomaticSpeechRecognition = async ( + data, + model = 'facebook/wav2vec2-large-960h-lv60-self', +) => { + const args = { data, model }; + const res = await callAPI(hf, 'hf.automaticSpeechRecognition', args); + return res; +}; + +// data = readFileSync('test/sample1.flac') + +const AudioClassification = async ( + data, + model = 'superb/hubert-large-superb-er', +) => { + const args = { data, model }; + const res = await callAPI(hf, 'hf.audioClassification', args); + return res; +}; + +// inputs = 'Hello world!' + +const TextToSpeech = async ( + inputs, + model = 'espnet/kan-bayashi_ljspeech_vits', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.textToSpeech', args); + return res; +}; +/* + data = readFileSync('test/sample1.flac') + */ +const AudioToAudio = async (data, model = 'speechbrain/sepformer-wham') => { + const args = { data, model }; + const res = await callAPI(hf, 'hf.audioToAudio', args); + return res; +}; + +//........Computer Vision +// data = readFileSync('test/cheetah.png') + +const ImageClassification = async ( + data, + model = 'google/vit-base-patch16-224', +) => { + const args = { data, model }; + const res = await callAPI(hf, 'hf.imageClassification', args); + return res; +}; +/* + data = readFileSync('test/cats.png') + */ +const ObjectDetection = async (data, model = 'facebook/detr-resnet-50') => { + const args = { data, model }; + const res = await callAPI(hf, 'hf.objectDetection', args); + return res; +}; + +// data = readFileSync('test/cats.png') + +const ImageSegmentation = async ( + data, + model = 'facebook/detr-resnet-50-panoptic', +) => { + const args = { data, model }; + const res = await callAPI(hf, 'hf.imageSegmentation', args); + return res; +}; + +// data = await (await fetch('https://picsum.photos/300/300')).blob() + +const ImageToText = async ( + data, + model = 'nlpconnect/vit-gpt2-image-captioning', +) => { + const args = { data, model }; + const res = await callAPI(hf, 'hf.imageToText', args); + return res; +}; + +/* + inputs = 'award winning high resolution photo of a giant' + + ' tortoise/((ladybird)) hybrid, [trending on artstation]', + parameters = {negative_prompt: 'blurry'}, +*/ + +const TextToImage = async ( + inputs, + parameters = {}, + model = 'stabilityai/stable-diffusion-2', +) => { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.textToImage', args); + return res; +}; + +/* + inputs = new Blob([readFileSync("test/stormtrooper_depth.png")]), + parameters = {prompt: "elmo's lecture"}, +*/ + +const ImageToImage = async ( + inputs, + parameters = {}, + model = 'lllyasviel/sd-controlnet-depth', +) => { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.imageToImage', args); + return res; +}; + +/* + inputs = { image: await (await fetch('https://placekitten.com/300/300')).blob() }, + parameters = { candidate_labels: ['cat', 'dog'] }, +*/ + +const ZeroShotImageClassification = async ( + inputs, + parameters = {}, + model = 'openai/clip-vit-large-patch14-336', +) => { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.zeroShotImageClassification', args); + return res; +}; + +//......Multimodal +// inputs = "That is a happy person", + +const FeatureExtraction = async ( + inputs, + model = 'sentence-transformers/distilbert-base-nli-mean-tokens', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.featureExtraction', args); + return res; +}; + +/* + inputs = { + question: 'How many cats are lying down?', + image: await (await fetch('https://placekitten.com/300/300')).blob() + }, +*/ + +const VisualQuestionAnswering = async ( + inputs, + model = 'dandelin/vilt-b32-finetuned-vqa', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.visualQuestionAnswering', args); + return res; +}; + +/* + inputs = { + question: 'Invoice number?', + image: await (await fetch('https://huggingface.co/spaces/impira/docquery/resolve/2359223c1837a7587402bda0f2643382a6eefeab/invoice.png')).blob(), + }, +*/ + +const DocumentQuestionAnswering = async ( + inputs, + model = 'impira/layoutlm-document-qa', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.documentQuestionAnswering', args); + return res; +}; + +//.....Tabular +/* + inputs = { + data: { + "Height": ["11.52", "12.48", "12.3778"], + "Length1": ["23.2", "24", "23.9"], + "Length2": ["25.4", "26.3", "26.5"], + "Length3": ["30", "31.2", "31.1"], + "Species": ["Bream", "Bream", "Bream"], + "Width": ["4.02", "4.3056", "4.6961"] + }, + }, +*/ + +const TabularRegression = async ( + inputs, + model = 'scikit-learn/Fish-Weight', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.tabularRegression', args); + return res; +}; + +/* + inputs = { + data: { + "fixed_acidity": ["7.4", "7.8", "10.3"], + "volatile_acidity": ["0.7", "0.88", "0.32"], + "citric_acid": ["0", "0", "0.45"], + "residual_sugar": ["1.9", "2.6", "6.4"], + "chlorides": ["0.076", "0.098", "0.073"], + "free_sulfur_dioxide": ["11", "25", "5"], + "total_sulfur_dioxide": ["34", "67", "13"], + "density": ["0.9978", "0.9968", "0.9976"], + "pH": ["3.51", "3.2", "3.23"], + "sulphates": ["0.56", "0.68", "0.82"], + "alcohol": ["9.4", "9.8", "12.6"] + }, + }, +*/ + +const TabularClassification = async ( + inputs, + model = 'vvmnnnkv/wine-quality', +) => { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.tabularClassification', args); + return res; +}; + +//........Custom +/* + inputs = "hello world", + parameters = { + custom_param: 'some magic', + } +*/ + +const CustomCall = async ( + inputs, + parameters = {}, + model = 'my-custom-model', +) => { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.request', args); + return res; +}; + +/* + inputs = "hello world", + parameters = { + custom_param: 'some magic', + } +*/ + +const CustomCallStreaming = async ( + inputs, + parameters = {}, + model = 'my-custom-model', +) => { + const args = { inputs, parameters, model }; + return this._makeApiCall('streamingRequest', args); +}; + +/* + inputs = 'The answer to the universe is', + endpoint = 'https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2' +*/ + +const CustomInferenceEndpoint = async (inputs, endpoint) => { + const args = { inputs }; + const hfEndpoint = hf.endpoint(endpoint); + try { + const res = await hfEndpoint.textGeneration(args); + return res; + } catch (err) { + throw new Error('Error occured while triggering "textGeneration" method', { + cause: err, + }); + } +}; + +module.exports = { + FillMask, + Summarization, + QuestionAnswering, + TableQuestionAnswering, + TextClassification, + TextGeneration, + TextGenerationStream, + TokenClassification, + Translation, + ZeroShotClassification, + SentenceSimilarity, + + AutomaticSpeechRecognition, + AudioClassification, + TextToSpeech, + AudioToAudio, + + ImageClassification, + ObjectDetection, + ImageSegmentation, + ImageToText, + TextToImage, + ImageToImage, + ZeroShotImageClassification, + FeatureExtraction, + + VisualQuestionAnswering, + DocumentQuestionAnswering, + TabularRegression, + TabularClassification, + CustomCall, + CustomCallStreaming, + CustomInferenceEndpoint, +}; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..2af991e --- /dev/null +++ b/lib/index.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = { + openai: require('./openai/connector.js'), + huggingface: require('./huggingface/connector.js'), + ollama: require('./ollama/connector.js'), + elevenlabs: require('./elevenlabs/connector.js'), +}; diff --git a/lib/ollama/connector.js b/lib/ollama/connector.js new file mode 100644 index 0000000..9d26a51 --- /dev/null +++ b/lib/ollama/connector.js @@ -0,0 +1,36 @@ +'use strict'; + +const { Ollama } = require('ollama'); +const { callAPI } = require('../common.js'); +const { ollamaOpts } = require('../../config.json'); + +const { transport, host, port } = ollamaOpts; +const ollama = new Ollama({ host: `${transport}://${host}:${port}` }); + +// (messages = [{ role: 'user', content: 'Why is the sky blue?' }], + +const completion = async ( + messages = [], + system = 'You are a useful assistant. You can answer questions,' + + ' provide information, and help with tasks.', + model = 'llama2', + stream = false, +) => { + const { error = null, message } = await callAPI(ollama, 'ollama.chat', { + messages: [ + { + role: 'system', + content: system, + }, + ...messages, + ], + model, + stream, + }); + if (error) throw error; + return message; +}; + +module.exports = { + completion, +}; diff --git a/lib/openai/config.json b/lib/openai/config.json new file mode 100644 index 0000000..b5e3e92 --- /dev/null +++ b/lib/openai/config.json @@ -0,0 +1,14 @@ +{ + "DEFAULT_MODELS": { + "completions": "gpt-4-turbo-2024-04-09", + "embedding": "text-embedding-3-small", + "classification": "text-moderation-007", + "fineTune": "gpt-3.5-turbo-0125", + "textToSpeech": "tts-1", + "speech": "whisper-1", + "vision": "gpt-4-vision-preview", + "imageCreate": "dall-e-3", + "image": "dall-e-2" + }, + "DEFAULT_VOICE": "onyx" +} diff --git a/lib/openai/connector.js b/lib/openai/connector.js new file mode 100644 index 0000000..b80b2a1 --- /dev/null +++ b/lib/openai/connector.js @@ -0,0 +1,154 @@ +'use strict'; + +const OpenAI = require('openai'); +const utils = require('./utils'); +const { measureTime, formatMeasureTime } = require('../common.js'); +const { DEFAULT_MODELS, DEFAULT_VOICE } = require('./config.json'); + +const { tokens, language, speech } = utils; + +class Chat { + //temperature = 0.7, topP = 1, frequencyPenalty = 0 + //presencePenalty = 0, stop = ["\n", ""] + constructor({ + apiKey, + system, + model = DEFAULT_MODELS.completions, + tools, + maxTokens = 1000, + // maxPrice = 0.1, + }) { + this.openai = new OpenAI({ apiKey }); + this.system = system; + this.model = model; + this.tools = tools; + this.maxTokens = maxTokens; + // this.maxPrice = maxPrice; + + this.messages = []; + this.tokens = 0; + this.price = 0; + + // throw new Error(`Max ${maxTokens} tokens exceeded`); + } + + async message({ text }) { + const tokenCount = tokens.count({ text, model: this.model }); + const { maxTokens, model } = this; + + const increaseMaxTokens = tokens + tokenCount > maxTokens; + if (increaseMaxTokens) { + throw new Error(`Max ${this.maxTokens} tokens exceeded`); + } + + const res = await language(this.openai).generate({ + text, + model, + messages: this.messages, + system: this.system, + tools: this.tools, + }); + + if (res.error) return res.error.message; + + this.messages = res.messages; + this.tokens += res.usage.total_tokens; + this.price += res.usage.total_price; + + return res.message; + } + + /* + "text" argument - in case we do conversion on front end + */ + async voiceMessage({ + text, + inputFilePath, + outputFilePath, + voice = DEFAULT_VOICE, + returnIntermediateResult = false, + }) { + const start = measureTime(); + let inputText = text; + + if (inputFilePath) { + inputText = await speech(this.openai).speechToText({ + pathToFile: inputFilePath, + }); + } + const outputText = await this.message({ text: inputText }); + if (returnIntermediateResult) { + return { + inputText, + outputText, + outputFilePath, + executionTime: formatMeasureTime(start), + }; + } + await speech(this.openai).textToSpeech({ + text: outputText, + pathToFile: outputFilePath, + voice, + }); + return { + inputText, + outputText, + outputFilePath, + executionTime: formatMeasureTime(start), + }; + } + + async voiceAnswer({ + inputText, + outputText, + outputFilePath, + voice = DEFAULT_VOICE, + }) { + const start = measureTime(); + await speech(this.openai).textToSpeech({ + text: outputText, + pathToFile: outputFilePath, + voice, + }); + return { + inputText, + outputText, + outputFilePath, + executionTime: formatMeasureTime(start), + }; + } +} + +class Assistant { + constructor({ + assistant_id, + thread_id, + /* + model = DEFAULT_MODELS.completions, + maxTokens = 1000, + maxPrice = 0.1, + */ + }) { + this.id = assistant_id; + this.thread_id = thread_id; + // this.model = model; + // this.maxTokens = maxTokens; + // this.maxPrice = maxPrice; + + this.messages = []; + // this.tokens = 0; + // this.price = 0; + } + + // message({ text }) {} +} + +module.exports = { + Chat, + Assistant, +}; + +// Class chat +// chat.message({text}) => {message, messages, usages} => string +// chat.voiceMessage({inputFilePath, outputFilePath, voice}) => +// {inputText, outputText, outputFilePath} diff --git a/lib/openai/data.js b/lib/openai/data.js new file mode 100644 index 0000000..ca5a8ff --- /dev/null +++ b/lib/openai/data.js @@ -0,0 +1,253 @@ +'use strict'; + +const models = { + completion: { + 'gpt-4-turbo-2024-04-09': { prices: { prompt: 10.0, completion: 30.0 } }, + 'gpt-4-1106-preview': { prices: { prompt: 10.0, completion: 30.0 } }, + 'gpt-3.5-turbo-0125': { prices: { prompt: 0.5, completion: 1.5 } }, + }, + //...No models in Tiktoken + embedding: { + 'text-embedding-3-small': { prices: { prompt: 0.02 }, dimension: 1536 }, + 'text-embedding-3-large': { prices: { prompt: 0.13 }, dimension: 3072 }, + 'text-embedding-ada-002': { prices: { prompt: 0.1 }, dimension: 1536 }, + }, + recognition: { + 'gpt-4-vision-preview': { + tokens: { base: 85, block: 170 }, + prices: { prompt: 10.0, completion: 30.0 }, + }, + }, + images: { + 'dall-e-3': { + prices: { + standard: { '1024×1024': 0.04, '1024×1792': 0.08, '1792×1024': 0.08 }, + hd: { '1024×1024': 0.08, '1024×1792': 0.12, '1792×1024': 0.12 }, + }, + }, + 'dall-e-2': { + prices: { + standard: { '256×256': 0.016, '512×512': 0.018, '1024×1024': 0.02 }, + }, + }, + }, + fineTune: { + 'gpt-3.5-turbo': { + prices: { training: 8.0, prompt: 3.0, completion: 6.0 }, + }, + 'davinci-002': { + prices: { training: 6.0, prompt: 12.0, completion: 12.0 }, + }, + 'babbage-002': { prices: { training: 0.4, prompt: 1.6, completion: 1.6 } }, + }, + speech: { + 'tts-1': { prices: { prompt: 15.0 } }, + 'tts-1-hd': { prices: { prompt: 30.0 } }, + 'whisper-1': { prices: { minute: 0.006 } }, + }, + assistants: { + assistant: { + prices: { 'code-interpreter-session': 0.03, 'retrieval-GB-day': 0.2 }, + }, + }, +}; + +/* +Rate limits are measured in five ways: +RPM (requests per minute), +RPD (requests per day), +TPM (tokens per minute), +TPD (tokens per day), +and IPM (images per minute). +Rate limits can be hit across any of the options depending on what occurs first. +For example, you might send 20 requests with only 100 tokens to the +ChatCompletions endpoint and that would fill your limit (if your RPM was 20), +even if you did not send 150k tokens (if your TPM limit was 150k) +within those 20 requests. + +Batch API queue limits are calculated based on the total number of input tokens +queued for a given model. + +Tokens from pending batch jobs are counted against your queue limit. +Once a batch job is completed, its tokens are no longer counted against +that model's limit. + +Other important things worth noting: + +Rate limits are defined at the organization level and at the project +level, not user level. +Rate limits vary by the model being used. +Limits are also placed on the total amount an organization can spend on +the API each month. These are also known as "usage limits". + +*/ + +const tiers = { + free: { + name: 'Free', + price: 0.0, + description: 'Free tier with limited access', + limits: { + 'gpt-3.5-turbo': { + RPM: 3, + RPD: 200, + TPM: 40000, + BATCH_QUEUE_LIMIT: 200000, + }, + 'text-embedding-3-small': { + RPM: 3, + RPD: 200, + TPM: 150000, + BATCH_QUEUE_LIMIT: null, + }, + 'whisper-1': { RPM: 3, RPD: 200, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1': { RPM: 3, RPD: 200, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-2': { + RPM: '5 img/min', + RPD: null, + TPM: null, + BATCH_QUEUE_LIMIT: null, + }, + 'dall-e-3': { + RPM: '1 img/min', + RPD: null, + TPM: null, + BATCH_QUEUE_LIMIT: null, + }, + }, + }, + tier1: { + name: 'Tier 1', + price: 10.0, + description: 'Paid tier with access to all models', + limits: { + 'gpt-4-turbo': { + RPM: 500, + RPD: null, + TPM: 300000, + BATCH_QUEUE_LIMIT: 900000, + }, + 'gpt-4': { RPM: 500, RPD: 10000, TPM: 10000, BATCH_QUEUE_LIMIT: 100000 }, + 'gpt-3.5-turbo': { + RPM: 3500, + RPD: 10000, + TPM: 60000, + BATCH_QUEUE_LIMIT: 200000, + }, + 'text-embedding-3-large': { + RPM: 500, + RPD: 10000, + TPM: 1000000, + BATCH_QUEUE_LIMIT: null, + }, + 'whisper-1': { RPM: 50, RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1': { RPM: 50, RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1-hd': { RPM: 3, RPD: null, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-2': { + RPM: '5 img/min', + RPD: null, + TPM: null, + BATCH_QUEUE_LIMIT: null, + }, + 'dall-e-3': { + RPM: '5 img/min', + RPD: null, + TPM: null, + BATCH_QUEUE_LIMIT: null, + }, + }, + }, + tier2: { + name: 'Tier 2', + price: 20.0, + description: 'Paid tier with access to all models and additional features', + limits: { + 'gpt-4-turbo': { RPM: 5000, TPM: 450000, BATCH_QUEUE_LIMIT: 1350000 }, + 'gpt-4': { RPM: 5000, TPM: 40000, BATCH_QUEUE_LIMIT: 200000 }, + 'gpt-3.5-turbo': { RPM: 3500, TPM: 80000, BATCH_QUEUE_LIMIT: 400000 }, + 'text-embedding-3-large': { + RPM: 500, + TPM: 1000000, + BATCH_QUEUE_LIMIT: null, + }, + 'whisper-1': { RPM: 50, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1': { RPM: 50, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1-hd': { RPM: 5, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-2': { RPM: '50 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-3': { RPM: '7 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, + }, + }, + tier3: { + name: 'Tier 3', + price: 30.0, + description: 'Paid tier with access to all models and additional features', + limits: { + 'gpt-4-turbo': { RPM: 5000, TPM: 600000, BATCH_QUEUE_LIMIT: 40000000 }, + 'gpt-4': { RPM: 5000, TPM: 80000, BATCH_QUEUE_LIMIT: 5000000 }, + 'gpt-3.5-turbo': { RPM: 3500, TPM: 160000, BATCH_QUEUE_LIMIT: 10000000 }, + 'text-embedding-3-large': { + RPM: 5000, + TPM: 5000000, + BATCH_QUEUE_LIMIT: null, + }, + 'whisper-1': { RPM: 100, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1': { RPM: 100, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1-hd': { RPM: 7, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-2': { RPM: '100 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-3': { RPM: '7 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, + }, + }, + tier4: { + name: 'Tier 4', + price: 40.0, + description: 'Paid tier with access to all models and additional features', + limits: { + 'gpt-4-turbo': { RPM: 10000, TPM: 800000, BATCH_QUEUE_LIMIT: 80000000 }, + 'gpt-4': { RPM: 10000, TPM: 300000, BATCH_QUEUE_LIMIT: 30000000 }, + 'gpt-3.5-turbo': { + RPM: 10000, + TPM: 1000000, + BATCH_QUEUE_LIMIT: 100000000, + }, + 'text-embedding-3-large': { + RPM: 10000, + TPM: 5000000, + BATCH_QUEUE_LIMIT: null, + }, + 'whisper-1': { RPM: 100, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1': { RPM: 100, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1-hd': { RPM: 10, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-2': { RPM: '100 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-3': { RPM: '15 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, + }, + }, + tier5: { + name: 'Tier 5', + price: 50.0, + description: 'Paid tier with access to all models and additional features', + limits: { + 'gpt-4-turbo': { RPM: 10000, TPM: 1500000, BATCH_QUEUE_LIMIT: 250000000 }, + 'gpt-4': { RPM: 10000, TPM: 300000, BATCH_QUEUE_LIMIT: 45000000 }, + 'gpt-3.5-turbo': { + RPM: 10000, + TPM: 2000000, + BATCH_QUEUE_LIMIT: 300000000, + }, + 'text-embedding-3-large': { + RPM: 10000, + TPM: 10000000, + BATCH_QUEUE_LIMIT: null, + }, + 'whisper-1': { RPM: 500, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1': { RPM: 500, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'tts-1-hd': { RPM: 20, TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-2': { RPM: '500 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, + 'dall-e-3': { RPM: '50 img/min', TPM: null, BATCH_QUEUE_LIMIT: null }, + }, + }, +}; + +module.exports = { + models, + tiers, +}; diff --git a/lib/openai/utils/assistants.js b/lib/openai/utils/assistants.js new file mode 100644 index 0000000..09c1442 --- /dev/null +++ b/lib/openai/utils/assistants.js @@ -0,0 +1,437 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +//.........ASSISTANTS............. +/* + The main advantage of an assistant in comparison to completeon + you don't have to send all the messages to OpenAI for each user interaction. + You can send only a new message to the assistant and OpenAI will keep + and manage the context of the conversation. + The disadvantage is - you can't add or remove some other content to the + conversation, like info from other models or any other data. + And it's still beta - sometimes it does not behave according to instructions +*/ + +/* + name = "Math Tutor", + instructions = + "You are a personal math tutor. " + + "Write and run code to answer math questions.", + tools = [{ type: "code_interpreter" }], + model = DEFAULT_MODELS.completions +*/ + +const assistants = (openai) => ({ + async create({ + name, + instructions, + tools = [], + model = DEFAULT_MODELS.completions, + }) { + // tools = {type:code_interpreter} + // {type:retrieval}, or {type:function, + // function:{name, description, parameters:{type, properties, required}}} + + const response = await callAPI( + openai.beta.assistants, + 'openai.beta.assistants.create', + { + name, + instructions, + tools, + model, + }, + ); + return response; + }, + + async list() { + const response = await callAPI( + openai.beta.assistants, + 'openai.beta.assistants.list', + ); + if (response.error) return response; + return response.data; + }, + + async retrieve({ assistant_id }) { + const response = await callAPI( + openai.beta.assistants, + 'openai.beta.assistants.retrieve', + assistant_id, + ); + return response; + }, + + async update({ assistant_id, name, instructions, tools, model, file_ids }) { + const current_assistant = await assistants.retrieve({ assistant_id }); + const args = { name, instructions, tools, model, file_ids }; + const props = {}; + for (const key in args) { + const arg = args[key]; + props[key] = arg || current_assistant[key]; + } + const response = await callAPI( + openai.beta.assistants, + 'openai.beta.assistants.update', + assistant_id, + props, + ); + return response; + }, + + async del({ assistant_id }) { + const response = await callAPI( + openai.beta.assistants, + 'openai.beta.assistants.del', + assistant_id, + ); + return response; + }, + + files: { + async create({ assistant_id, file_id }) { + //purpose should be "assistants" + const file = await callAPI( + openai.beta.assistants.files, + 'openai.beta.assistants.files.create', + assistant_id, + { file_id }, + ); + return file; + }, + + async list({ assistant_id }) { + const files = await callAPI( + openai.beta.assistants.files, + 'openai.beta.assistants.files.list', + assistant_id, + ); + if (files.error) return files; + return files.data; + }, + + async retrieve({ assistant_id, file_id }) { + const file = await callAPI( + openai.beta.assistants.files, + 'openai.beta.assistants.files.retrieve', + assistant_id, + file_id, + ); + return file; + }, + + async del({ assistant_id, file_id }) { + const file = await callAPI( + openai.beta.assistants.files, + 'openai.beta.assistants.files.del', + assistant_id, + file_id, + ); + return file; + }, + }, + + threads: { + async create(/*{ messages = [] }*/) { + const response = await callAPI( + openai.beta.threads, + 'openai.beta.threads.create', + ); + return response; + }, + + async createAndRun({ assistant_id, thread = { messages: [] } }) { + const response = await callAPI( + openai.beta.threads, + 'openai.beta.threads.createAndRun', + { assistant_id, thread }, + ); + return response; + }, + + /* + id = 'thread_D1Fc45AQAhZsywNdSAGReFpM' + */ + async retrieve({ thread_id }) { + const response = await callAPI( + openai.beta.threads, + 'openai.beta.threads.retrieve', + thread_id, + ); + return response; + }, + + async update({ thread_id, params = {} }) { + const response = await callAPI( + openai.beta.threads, + 'openai.beta.threads.update', + thread_id, + params, + ); + return response; + }, + + async del({ thread_id }) { + const response = await callAPI( + openai.beta.threads, + 'openai.beta.threads.del', + thread_id, + ); + return response; + }, + + messages: { + async create({ thread_id, role = 'user', content = '' }) { + // return console.log({thread_id, role, content}); + const response = await callAPI( + openai.beta.threads.messages, + 'openai.beta.threads.messages.create', + thread_id, + { + role, + content, + }, + ); + return response; + }, + + async list({ thread_id, clean = false }) { + const response = await callAPI( + openai.beta.threads.messages, + 'openai.beta.threads.messages.list', + thread_id, + ); + if (response.error) return response; + if (clean) { + const messages = []; + for (const { data } of response) { + messages.push(data.content); + } + // console.log({ + // message0: messages[0][0].text, + // message1: messages[1][0].text, + // }); + return messages; + } + return response; + }, + + async listFiles({ thread_id, message_id }) { + const response = await callAPI( + openai.beta.threads.messages, + 'openai.beta.threads.messages.files.list', + thread_id, + message_id, + ); + if (response.error) return response; + return response.data; + }, + + async retrieve({ thread_id, message_id }) { + const response = await callAPI( + openai.beta.threads.messages, + 'openai.beta.threads.messages.retrieve', + thread_id, + message_id, + ); + return response; + }, + + async retrieveFile({ thread_id, message_id, file_id }) { + const response = await callAPI( + openai.beta.threads.messages.files, + 'openai.beta.threads.messages.files.retrieve', + thread_id, + message_id, + file_id, + ); + return response; + }, + + async update({ + thread_id, + message_id, + params = { + metadata: { + metadata: { + modified: 'true', + user: 'test', + }, + }, + }, + }) { + const response = await await callAPI( + openai.beta.threads.messages, + 'openai.beta.threads.messages.update', + thread_id, + message_id, + params, + ); + return response; + }, + }, + + runs: { + async create({ thread_id, assistant_id }) { + const response = await callAPI( + openai.beta.threads.runs, + 'openai.beta.threads.runs.create', + thread_id, + { + assistant_id, + }, + ); + return response; + }, + + async list({ thread_id }) { + const response = await callAPI( + openai.beta.threads.runs, + 'openai.beta.threads.runs.list', + thread_id, + ); + if (response.error) return response; + return response.data; + }, + + async retrieve({ thread_id, run_id }) { + const response = await callAPI( + openai.beta.threads.runs, + 'openai.beta.threads.runs.retrieve', + thread_id, + run_id, + ); + return response; + }, + + // .....logs + async listRunSteps({ thread_id, run_id, /* limit = 20,*/ clean }) { + const response = await callAPI( + openai.beta.threads.runs.steps, + 'openai.beta.threads.runs.steps.list', + thread_id, + run_id, + ); + if (response.error) return response; + if (!clean) return response; + const size = response.data.length; + const steps = new Array(size).fill(Object.create(null)); + for (let i = 0; i < size; i++) { + const { step_details: details, usage } = response.data[i]; + steps[i] = { details, usage }; + } + return steps; + }, + + async retrieveRunStep({ thread_id, run_id, step_id }) { + const response = await callAPI( + openai.beta.threads.runs.steps, + 'openai.beta.threads.runs.steps.retrieve', + thread_id, + run_id, + step_id, + ); + return response; + }, + + async update({ thread_id, run_id, props = {} }) { + const response = await callAPI( + openai.beta.threads.runs, + 'openai.beta.threads.runs.update', + thread_id, + run_id, + props, + ); + return response; + }, + /* + When a run has the status: "requires_action" and required_action. + type is submit_tool_outputs, + this endpoint can be used to submit the outputs from the tool + calls once they're all completed. + All outputs must be submitted in a single request. + Like this + { + tool_outputs: [ + { + tool_call_id: "call_001", + output: "70 degrees and sunny.", + }, + ], + } + */ + async submitToolOutput({ + thread_id, + run_id, + output = { tool_outputs: [] }, + }) { + const response = await callAPI( + openai.beta.threads.runs.steps.tools, + 'openai.beta.threads.runs.steps.tools.submit', + thread_id, + run_id, + output, + ); + return response; + }, + + async cancel({ thread_id, run_id }) { + const response = await callAPI( + openai.beta.threads.runs, + 'openai.beta.threads.runs.cancel', + thread_id, + run_id, + ); + return response; + }, + }, + }, +}); + +module.exports = { assistants }; + +// assistants.create({name, instructions, tools, model}) => response +// assistants.list() => data (array) +// assistants.retrieve({assistant_id}) => response +// assistants.update({ +// assistant_id, name, instructions, tools, model, file_ids +// }) => response +// assistants.del({assistant_id}) => response + +// assistants.files.create({file_id}) => response +// assistants.files.list({assistant_id}) => data (array) +// assistants.files.retrieve({assistant_id, file_id}) => response +// assistants.files.del({assistant_id, file_id}) => response + +// assistants.threads.create({messages = []}) => response +// assistants.threads.createAndRun({ +// assistant_id, thread = {messages:[]} +// }) => response +// assistants.threads.retrieve({thread_id}) => response +// assistants.threads.update({thread_id, messages}) => response +// assistants.threads.del({thread_id}) => response + +// assistants.threads.messages.create({thread_id, role, content}) => response +// assistants.threads.messages.list({thread_id, clean}) => data(array) +// assistants.threads.messages.listFiles({thread_id, message_id}) => +// data (array) //does not work +// assistants.threads.messages.retrieve({thread_id, message_id}) => response +// assistants.threads.messages.retrieveFile({thread_id, message_id, file_id}) +// => response +// assistants.threads.messages.update({thread_id, message_id, user_id}) => +// response + +// assistants.threads.runs.create({thread_id, assistant_id}) => response +// assistants.threads.runs.list({thread_id}) => response +// assistants.threads.runs.retrieve({thread_id, run_id}) => response +// assistants.threads.runs.listRunSteps({thread_id, run_id, limit, clean}) => +// response +// assistants.threads.runs.retrieveRunStep({thread_id, run_id, step_id}) => +// response +// assistants.threads.runs.update({thread_id, run_id, data}) => response +// assistants.threads.runs.submitToolOutput({thread_id, run_id, output}) => +// response +// assistants.threads.runs.cancel({thread_id, run_id}) => response diff --git a/lib/openai/utils/files.js b/lib/openai/utils/files.js new file mode 100644 index 0000000..13c2df2 --- /dev/null +++ b/lib/openai/utils/files.js @@ -0,0 +1,64 @@ +'use strict'; + +const fs = require('node:fs'); +const { callAPI, processFileLineByLine } = require('../../common.js'); +const { tokens } = require('./tokens.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const files = (openai) => ({ + async countFileTokens({ pathToFile, model = DEFAULT_MODELS.completions }) { + let tokensNumber = 0; + await processFileLineByLine( + pathToFile, + ({ line }) => void (tokensNumber += tokens.count({ text: line, model })), + console.log, + ); + return tokensNumber; + }, + + async create({ pathToFile, purpose = 'fine-tune' }) { + // purposes = "fine-tune", "assistants" + const response = await callAPI(openai.files, 'openai.files.create', { + file: fs.createReadStream(pathToFile), + purpose, + }); + return response; + }, + + async list() { + const response = await callAPI(openai.files, 'openai.files.list'); + if (response.error) return response; + return response.data; + }, + + async retrieve({ file_id }) { + const response = await callAPI( + openai.files, + 'openai.files.retrieve', + file_id, + ); + return response; + }, + + async content({ file_id }) { + const file = await callAPI( + openai.files, + 'openai.files.retrieveContent', + file_id, + ); + return file; + }, + + async del({ file_id }) { + const file = await callAPI(openai.files, 'openai.files.del', file_id); + return file; + }, +}); + +module.exports = { files }; + +// files.create({pathToFile, purpose}) => response +// files.list() => data (array) +// files.retrieve({file_id}) => response +// files.content({file_id}) => response +// files.del({file_id}) => response diff --git a/lib/openai/utils/finetune.js b/lib/openai/utils/finetune.js new file mode 100644 index 0000000..c7c98b0 --- /dev/null +++ b/lib/openai/utils/finetune.js @@ -0,0 +1,112 @@ +'use strict'; + +const { files } = require('./files.js'); +const { callAPI } = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const fineTune = (openai) => ({ + // hyperparameters = + // batch_size + // string or integer Optional Defaults to auto + // Number of examples in each batch. A larger batch size means that model + // parameters are updated less frequently, but with lower variance. + + // learning_rate_multiplier string or number Optional Defaults to auto + // Scaling factor for the learning rate. A smaller learning + // rate may be useful to avoid overfitting. + + // n_epochs + // string or integer Optional Defaults to auto + // The number of epochs to train the model for. + // An epoch refers to one full cycle through the training dataset. + async create({ + pathToFile, + training_file, + hyperparameters = { + batch_size: 'auto', + learning_rate_multiplier: 'auto', + n_epochs: 'auto', + }, + suffix = '', + model = DEFAULT_MODELS.fineTune, + deleteFile = false, + maxTokens = 0, + }) { + if (pathToFile) { + const epochs = + hyperparameters.n_epochs !== 'auto' ? hyperparameters.n_epochs : 1; + const tokens = await files(openai).countFileTokens({ pathToFile }); + if (maxTokens && tokens * epochs > maxTokens) { + throw new Error(`Max tokens ${maxTokens} exceeded ${tokens}`); + } + const file = await files(openai).create({ pathToFile }); + if (!file || !file.id) return console.error('Error creating file'); + training_file = file.id; + } + const params = { training_file, suffix, model }; + if (hyperparameters) params.hyperparameters = hyperparameters; + if (suffix) params.suffix = suffix; + + const response = await callAPI( + openai.fineTuning.jobs, + 'openai.fineTuning.jobs.create', + params, + ); + if (response.error) return response; + if (deleteFile) await files(openai).del({ training_file }); + + return response; + }, + + async list() { + const response = await callAPI( + openai.fineTuning.jobs, + 'openai.fineTuning.jobs.list', + ); + if (response.error) return response; + return response.data; + // for await (const fineTune of list) { + // console.log(fineTune); + // } + }, + + async events({ id, limit = 2 }) { + const list = await callAPI( + openai.fineTuning, + 'openai.fineTuning.list_events', + id, + limit, + ); + + // for await (const fineTune of list) { + // console.log(fineTune); + // } + return list; + }, + + async retrieve({ id }) { + const res = await callAPI( + openai.fineTuning.jobs, + 'openai.fineTuning.jobs.retrieve', + id, + ); + return res; + }, + + async cancel({ id }) { + const fineTune = await callAPI( + openai.fineTuning.jobs, + 'openai.fineTuning.jobs.cancel', + id, + ); + return fineTune; + }, +}); + +module.exports = { fineTune }; + +// fineTune.create({pathToFile, training_file, hyperparameters, suffix, +// fineTune.list() => data (array) +// fineTune.events({id, limit}) => response - does not work +// fineTune.retrieve({id}) => response +// fineTune.cancel{({id}) => response diff --git a/lib/openai/utils/images.js b/lib/openai/utils/images.js new file mode 100644 index 0000000..12cfcc8 --- /dev/null +++ b/lib/openai/utils/images.js @@ -0,0 +1,114 @@ +'use strict'; + +const fs = require('node:fs'); +const { callAPI, saveFileFromWeb } = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const images = (openai) => ({ + /* + + prompt: "a white siamese cat", + model: "dall-e-3", + n: 1, (dalle-3 always generates only one image per request) + size: "1024x1024", + + */ + async create({ + text, + saveAs = '', + size = '1024x1024', + quality = 'standard', + n = 1, + model = DEFAULT_MODELS.imageCreate, + }) { + //quality: "standard", "hd" + // return console.log(pathToFile.replace(/^\./, '')); + + const response = await callAPI(openai.images, 'openai.images.generate', { + prompt: text, + size, + quality, + n, + model, + }); + if (response.error) return response; + const url = response?.data[0]?.url; + // const url = `https://oaidalleapiprodscus.blob.core.windows.net/private/org-0W2DSt5sTB0F2NuqbNIHHcTg/user-m7HmI4bqRdNZxYTHJlFPLN9P/img-nYmIPv103J4yZAw29MTr7N7x.png?st=2024-03-24T17%3A16%3A00Z&se=2024-03-24T19%3A16%3A00Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-03-23T21%3A30%3A02Z&ske=2024-03-24T21%3A30%3A02Z&sks=b&skv=2021-08-06&sig=ByJlRNd/IQsOrgmruP8u/oyjWmKE/PDW4tG9E1YuoRo%3D`; + if (url && saveAs.length) { + try { + await saveFileFromWeb(url, saveAs); + } catch (error) { + console.error('Error saving file:', error); + } + } + return { url, local: saveAs.replace(/^\./, '') }; + }, + /* + accepts only .png RGBA images + */ + async edit({ + text, + pathToFile, + pathToMask = '', + saveAs = '', + size = '1024x1024', + n = 1, + model = DEFAULT_MODELS.image, + }) { + const params = { + prompt: text, + image: fs.createReadStream(pathToFile), + model, + n, + size, + }; + if (pathToMask.length) params.mask = fs.createReadStream(pathToMask); + + const response = await callAPI(openai.images, 'openai.images.edit', params); + if (response.error) return response; + const url = response.data[0].url; + if (url && saveAs.length) { + try { + await saveFileFromWeb(url, saveAs); + } catch (error) { + return console.error('Error saving file:', error); + } + } + return { url, local: saveAs.replace(/^\./, '') }; + }, + + async variation({ + pathToFile, + saveAs = '', + size = '1024x1024', + n = 1, + model = DEFAULT_MODELS.image, + }) { + const response = await callAPI( + openai.images, + 'openai.images.createVariation', + { + image: fs.createReadStream(pathToFile), + model, + n, + size, + }, + ); + if (response.error) return response; + const url = response.data[0].url; + if (url && saveAs.length) { + try { + await saveFileFromWeb(url, saveAs); + } catch (error) { + return console.error('Error saving file:', error); + } + } + return { url, local: saveAs.replace(/^\./, '') }; + }, +}); + +module.exports = { images }; + +// images.create({text, pathToFile, size, quality, n, model}) => {url, local} +// images.edit({text, pathToFile, pathToMask, size, n, model}) => response +// images.variation({pathToFile, size, n, model}) => response diff --git a/lib/openai/utils/index.js b/lib/openai/utils/index.js new file mode 100644 index 0000000..a522d44 --- /dev/null +++ b/lib/openai/utils/index.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = { + ...require('./assistants.js'), + ...require('./files.js'), + ...require('./finetune.js'), + ...require('./images.js'), + ...require('./language.js'), + ...require('./models.js'), + ...require('./price.js'), + ...require('./recognation.js'), + ...require('./speech.js'), + ...require('./tokens.js'), + ...require('./tools.js'), +}; diff --git a/lib/openai/utils/language.js b/lib/openai/utils/language.js new file mode 100644 index 0000000..b0099ba --- /dev/null +++ b/lib/openai/utils/language.js @@ -0,0 +1,167 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); +const { price } = require('./price.js'); +const { defineTools } = require('./tools.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +//......Natural Language Processing (language)....... + +/* +you should accumulate the messages in the conversation and +send them all at once to the completion endpoint. + +Example of messages: + [{ + "role": "user", + "content": "Hello, I'm a user." + },{ + "role": "assistant", + "content": "Hello, how can I help you?" + },{ + "role": "user", + "content": "Why roses are red?" + }] + + Models: + gpt-3.5-turbo - cheaper, less advanced + gpt-4-turbo-2024-04-09 - more advanced, more expensive + +*/ + +const language = (openai) => ({ + async generate({ + text, + messages = [], + system = 'You are a useful assistant.' + + ' You can answer questions, provide information, and help with tasks.', + model = DEFAULT_MODELS.completions, + tools = [], + tool_choice = 'auto', + }) { + const currentMessages = [ + ...messages, + { + role: 'user', + content: text, + }, + ]; + + const params = { + messages: [ + { + role: 'system', + content: system, + }, + ...currentMessages, + ], + model, + }; + + let defs = { tools: [], functions: {} }; + if (Array.isArray(tools) && tools.length > 0) { + defs = defineTools(tools); + params.tools = defs.tools; + params.tool_choice = tool_choice; + } + // return params; + let completion = await callAPI( + openai.chat.completions, + 'openai.chat.completions.create', + params, + ); + if (completion.error) return completion; + + let responseMessage = completion.choices[0].message; + + const usages = [completion.usage]; + + if (responseMessage.tool_calls) { + params.messages.push(responseMessage); + + for (const toolCall of responseMessage.tool_calls) { + const functionName = toolCall.function.name; + const functionToCall = defs.functions[functionName]; + if (!functionToCall) { + throw new Error(`Function ${functionName} not found`); + } + + const { fn, scope } = functionToCall; + const args = JSON.parse(toolCall.function.arguments); + console.log({ functionName, functionToCall, args }); + const functionResponse = await fn.call(scope, args); + console.log({ functionResponse }); + + params.messages.push({ + tool_call_id: toolCall.id, + role: 'tool', + name: functionName, + content: functionResponse, + }); + } + + completion = await callAPI( + openai.chat.completions, + 'openai.chat.completions.create', + params, + ); + if (completion.error) return completion; + responseMessage = completion.choices[0].message; + usages.push(completion.usage); + + // console.log({completion, responseMessage}) + } + + currentMessages.push(responseMessage); + const usageAndPrices = price.calculateByUsage({ + purpose: 'completion', + model, + usages, + }); + + return { + message: responseMessage.content, + messages: currentMessages, + usage: usageAndPrices, + }; + }, + + async embedding({ text = 'Sample text', model = DEFAULT_MODELS.embedding }) { + const response = await callAPI( + openai.embeddings, + 'openai.embeddings.create', + { + model, + input: text, + encoding_format: 'float', + }, + ); + if (response.error) return response; + const usageAndPrices = price.calculateByUsage({ + purpose: 'embedding', + model, + usage: response.usage, + }); + return { embedding: response.data[0].embedding, usage: usageAndPrices }; + }, + + async classification({ text, model = DEFAULT_MODELS.classification }) { + const response = await callAPI( + openai.moderations, + 'openai.moderations.create', + { + input: text, + model, + }, + ); + if (response.error) return response; + return response.results[0]; + }, +}); + +module.exports = { language }; + +// language.generate({text, messages, system, model, tools, tool_choice}) => +// {message, messages, usages} +// language.embedding({text, model}) => {embedding, usage} +// language.classification({text, model}) => response diff --git a/lib/openai/utils/models.js b/lib/openai/utils/models.js new file mode 100644 index 0000000..619938e --- /dev/null +++ b/lib/openai/utils/models.js @@ -0,0 +1,39 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); + +const models = (openai) => ({ + async list() { + const response = await callAPI(openai.models, 'openai.models.list'); + if (response.error) return response; + return response.data; + // for await (const fineTune of list) { + // console.log(fineTune); + // } + }, + + async retrieve({ model_id }) { + const response = await callAPI( + openai.models, + 'openai.models.retrieve', + model_id, + ); + return response; + }, + + async del({ model_id }) { + const response = await callAPI( + openai.models, + 'openai.models.del', + model_id, + ); + return response; + }, +}); + +module.exports = { models }; + +// models.list() => response +// models.retrieve({model_id}) => response +// models.del({model_id}) => response +// model, deleteFile, maxTokens}) => response diff --git a/lib/openai/utils/price.js b/lib/openai/utils/price.js new file mode 100644 index 0000000..e16debc --- /dev/null +++ b/lib/openai/utils/price.js @@ -0,0 +1,241 @@ +'use strict'; + +const fromExponential = require('from-exponential'); +const sharp = require('sharp'); +const { parseFile } = require('music-metadata'); +const { tokens } = require('./tokens.js'); +const openaiData = require('../data.js'); +const { math } = require('../../common.js'); + +const price = { + async estimate({ + purpose, + model, + text = '', + pathToFile = '', + options = { + type: 'prompt', + detail: 'low', + resolution: '1024x1024', + quality: 'standard', + }, + }) { + const res = { price: 0, tokens: 0, options }; + + let tokenCost = 0, + metadata; + if (text.length) res.tokens = tokens.count({ text, model }); + + const usedModel = openaiData.models[purpose][model]; + const prices = {}; + const expectedKeys = ['training', 'prompt', 'completion']; + for (const key in usedModel.prices) { + if (expectedKeys.includes(key)) + prices[key] = math.divide(usedModel.prices[key], 1000000); + } + + if (purpose === 'completeon') { + tokenCost = options.type === 'prompt' ? prices.prompt : prices.completion; + res.price = math.multiply(res.tokens, tokenCost); + } else if (purpose === 'embedding') { + tokenCost = prices.prompt; + res.price = math.multiply(res.tokens, tokenCost); + } else if (purpose === 'recognition') { + res.tokens += usedModel.tokens.base; + if (options.detail !== 'low') { + metadata = await sharp(pathToFile).metadata(); + const scaled = price._scaleAndMeasureImage( + metadata.width, + metadata.height, + ); + // console.log(metadata.width, metadata.height, scaled); + res.tokens += scaled.numberOfSquares * usedModel.tokens.block; + } + res.price = math.multiply(res.tokens, prices.prompt); + } else if (purpose === 'images') { + res.price = usedModel.prices[options.quality][options.resolution]; + } else if (purpose === 'fineTune') { + if (options.type === 'training') { + // TODO: files not defined + // res.tokens = await files.countFileTokens({ pathToFile, model }); + // res.price = math.multiply(res.tokens, prices.training); + } else { + const token_cost = + options.type === 'prompt' ? prices.prompt : prices.completion; + res.price = math.multiply(res.tokens, token_cost); + } + } else if (purpose === 'speech') { + if (model === 'whisper-1') { + metadata = await parseFile(pathToFile); + res.duration = Math.round(metadata.format.duration); + res.price = math.multiply(res.duration, usedModel.prices.minute / 60); + // console.log(inspect(metadata, { showHidden: false, depth: null })); + } else { + res.price = math.multiply(res.tokens, prices.prompt); + } + } + // console.log(usedModel.prices) + + // switch (purpose) { + // case 'completeon': + // tokenCost = + // options.type === 'prompt' ? prices.prompt : prices.completion; + // res.price = math.multiply(res.tokens, tokenCost); + // break; + // case 'embedding': + // tokenCost = prices.prompt; + // res.price = math.multiply(res.tokens, tokenCost); + // break; + // case 'recognition': + // res.tokens += usedModel.tokens.base; + // if (options.detail !== 'low') { + // metadata = await sharp(pathToFile).metadata(); + // const scaled = price._scaleAndMeasureImage( + // metadata.width, + // metadata.height, + // ); + // // console.log(metadata.width, metadata.height, scaled); + // res.tokens += scaled.numberOfSquares * usedModel.tokens.block; + // } + // + // res.price = math.multiply(res.tokens, prices.prompt); + // break; + // + // case 'images': + // res.price = usedModel.prices[options.quality][options.resolution]; + // break; + // + // case 'fineTune': + // if (options.type === 'training') { + // res.tokens = await files.countFileTokens({ pathToFile, model }); + // res.price = math.multiply(res.tokens, prices.training); + // } else { + // const token_cost = + // options.type === 'prompt' ? prices.prompt : prices.completion; + // res.price = math.multiply(res.tokens, token_cost); + // } + // break; + // + // case 'speech': + // if (model === 'whisper-1') { + // metadata = await parseFile(pathToFile); + // res.duration = Math.round(metadata.format.duration); + // res.price = math.multiply( + // res.duration, + // usedModel.prices.minute / 60 + // ); + // // console.log( + // inspect(metadata, { showHidden: false, depth: null }) + // ); + // } else { + // res.price = math.multiply(res.tokens, prices.prompt); + // } + // + // break; + // } + // res.string = res.price.toFixed(20).replace(/[0]+$/, ''); + res.string = fromExponential(res.price); + return res; + }, + + async calculateByUsage({ purpose, model, usage, usages }) { + const usedModel = openaiData.models[purpose][model]; + const prices = {}; + for (const key in usedModel.prices) { + if (['training', 'prompt', 'completion'].includes(key)) + prices[key] = math.divide(usedModel.prices[key], 1000000); + } + + const tokens = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }; + let total = 0; + let prompt = 0; + let completion = 0; + let res = {}; + + if (usage) { + const { prompt_tokens, completion_tokens } = usage; + prompt = math.multiply(prompt_tokens, prices.prompt); + completion = math.multiply(completion_tokens, prices.completion); + total = math.add(prompt, completion); + res = { ...usage }; + } else if (usages) { + for (const usage of usages) { + const { prompt_tokens, completion_tokens } = usage; + tokens.prompt_tokens += prompt_tokens; + tokens.completion_tokens += completion_tokens; + tokens.total_tokens += math.add(prompt_tokens, completion_tokens); + prompt += math.multiply(prompt_tokens, prices.prompt); + completion += math.multiply(completion_tokens, prices.completion); + } + total = math.add(prompt, completion); + res = tokens; + } + res.prompt_price = prompt; + res.completion_price = completion; + res.total_price = total; + // res.prices = { prompt, completion, total }; + // res.strings = { prompt:fromExponential(prompt), + // completion:fromExponential(completion), total:fromExponential(total) }; + return res; + }, + + _scaleAndMeasureImage(width, height) { + // Define the constraints + const maxLongSide = 2048; + const maxShortSide = 768; + const squareSize = 512; + + // Step 1: Scale down if the longest side is more than 2048 pixels + const aspectRatio = width / height; + if (width > height) { + if (width > maxLongSide) { + width = maxLongSide; + height = width / aspectRatio; + } + } else if (height > maxLongSide) { + height = maxLongSide; + width = height * aspectRatio; + } + + // Step 2: Scale down further if the shortest side + // is more than 768 pixels after step 1 + if (Math.min(width, height) > maxShortSide) { + if (width < height) { + width = maxShortSide; + height = width / aspectRatio; + } else { + height = maxShortSide; + width = height * aspectRatio; + } + } + + // Make sure the final dimensions are rounded to avoid fractional pixels + width = Math.round(width); + height = Math.round(height); + + // Step 3: Calculate the number of squares needed + const squaresWidth = Math.ceil(width / squareSize); + const squaresHeight = Math.ceil(height / squareSize); + const totalSquares = squaresWidth * squaresHeight; + + return { + scaledWidth: width, + scaledHeight: height, + numberOfSquares: totalSquares, + }; + }, +}; + +module.exports = { price }; + +// price.calculate({model, input, type}) => number +// price.estimate({purpose, model, text = '', pathToFile = '', +// options = {type: 'prompt', detail:'low', resolution:"1024x1024", +// quality:'standard'}}) => {price:0, tokens:0, options} +// price.calculate({purpose, model, usage}) => { +// prompt_token, +// completion_token, +// total_tokens, +// prices: { prompt, completion, total }, +// strings: { prompt, completion, total } +// } diff --git a/lib/openai/utils/recognation.js b/lib/openai/utils/recognation.js new file mode 100644 index 0000000..0efe06f --- /dev/null +++ b/lib/openai/utils/recognation.js @@ -0,0 +1,112 @@ +'use strict'; + +const fs = require('node:fs'); +const path = require('node:path'); +const { + callAPI, + cleanDirectory, + extractVideoFrames, +} = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const recognition = (openai) => ({ + async image({ + url, + pathToFile, + prompt = 'What’s in this image?', + detail = 'auto', + max_tokens = 300, + model = DEFAULT_MODELS.vision, + }) { + let imageURL = url; + if (!imageURL) { + if (!pathToFile) throw new Error('No image provided'); + const data = await fs.promises.readFile(pathToFile); + const base64Image = Buffer.from(data, 'binary').toString('base64'); + imageURL = `data:image/jpeg;base64, ${base64Image}`; + } + const response = await callAPI( + openai.chat.completions, + 'openai.chat.completions.create', + { + model, + messages: [ + { + role: 'user', + content: [ + { type: 'text', text: prompt }, + { + type: 'image_url', + image_url: { + url: imageURL, + detail, //low, high, or auto + }, + }, + ], + }, + ], + max_tokens, + }, + ); + if (response.error) return response; + return { + message: response.choices[0].message.content, + usage: response.usage, + }; + }, + /* + Requires ffmpeg installed + May exceed the max tokens per minutes limit + */ + async video({ + pathToFile, + outputDir, + max_tokens = 300, + frameRate = 1, + model = DEFAULT_MODELS.vision, + }) { + await cleanDirectory(outputDir); + await extractVideoFrames(pathToFile, outputDir, frameRate); + const files = await fs.promises.readdir(outputDir); + const content = [ + { + type: 'text', + text: + 'These are frames from a video. ' + + 'Generate a compelling description of the video.', + }, + ]; + for (const file of files) { + const data = await fs.promises.readFile(path.join(outputDir, file)); + const base64Image = Buffer.from(data, 'binary').toString('base64'); + const imageURL = `data:image/jpeg;base64, ${base64Image}`; + content.push({ type: 'image_url', image_url: { url: imageURL } }); + } + + const response = await callAPI( + openai.chat.completions, + 'openai.chat.completions.create', + { + model, + messages: [ + { + role: 'user', + content, + }, + ], + max_tokens, + }, + ); + if (response.error) return response; + return { + message: response.choices[0].message.content, + usage: response.usage, + }; + }, +}); + +module.exports = { recognition }; +// recognition.image({url, pathToFile, prompt, max_tokens, model}) => +// {message, usage} +// recognition.video({pathToFile, outputDir, max_tokens, model}) => +// {message, usage} diff --git a/lib/openai/utils/speech.js b/lib/openai/utils/speech.js new file mode 100644 index 0000000..7d63be0 --- /dev/null +++ b/lib/openai/utils/speech.js @@ -0,0 +1,79 @@ +'use strict'; + +const fs = require('node:fs'); +const path = require('node:path'); +const OpenAI = require('openai'); +const { callAPI } = require('../../common.js'); +const { DEFAULT_MODELS, DEFAULT_VOICE } = require('../config.json'); + +const speech = (openai) => ({ + /* + voices: alloy, echo, fable, onyx, nova, and shimmer + speed: 0.25 - 4.0 + model: tts-1, tts-1-hd + */ + + async textToSpeech({ + text, + pathToFile = './tests/test-speech.mp3', + voice = DEFAULT_VOICE, + speed = 1.0, + model = DEFAULT_MODELS.textToSpeech, + }) { + const speechFile = path.resolve(pathToFile); + const mp3 = await callAPI( + openai.audio.speech, + 'openai.audio.speech.create', + { + input: text, + voice, + speed, + model, + }, + ); + if (mp3.error) return mp3; + const buffer = Buffer.from(await mp3.arrayBuffer()); + await fs.promises.writeFile(speechFile, buffer); + return buffer; + }, + + async speechToText({ + pathToFile = './tests/test-speech.mp3', + model = DEFAULT_MODELS.speech, + }) { + const buffer = await fs.promises.readFile(path.resolve(pathToFile)); + const transcription = await callAPI( + openai.audio.transcriptions, + 'openai.audio.transcriptions.create', + { + file: await OpenAI.toFile(buffer, path.basename(pathToFile)), + model, + }, + ); + if (transcription.error) return transcription; + return transcription.text; + }, + + async speechTranslation({ + pathToFile = './tests/test-speech.mp3', + model = DEFAULT_MODELS.speech, + }) { + const translation = await callAPI( + openai.audio.translations, + 'openai.audio.translations.create', + { + file: fs.createReadStream(pathToFile), + model, + }, + ); + if (translation.error) return translation; + return translation.text; + }, +}); + +module.exports = { speech }; + +// speech.textToSpeech({text, pathToFile, voice, model}) => buffer +// speech.speechToText({pathToFile, model}) => string +// speech.speechTranslation({pathToFile, model}) => +// string // does not translate diff --git a/lib/openai/utils/tokens.js b/lib/openai/utils/tokens.js new file mode 100644 index 0000000..1629810 --- /dev/null +++ b/lib/openai/utils/tokens.js @@ -0,0 +1,27 @@ +'use strict'; + +const { encoding_for_model } = require('tiktoken'); +const { Tiktoken } = require('tiktoken/lite'); +const cl100k_base = require('tiktoken/encoders/cl100k_base.json'); + +const tokens = { + count({ text, model = 'gpt-4-turbo-2024-04-09' }) { + let enc; + try { + enc = encoding_for_model(model); + } catch (error) { + enc = new Tiktoken( + cl100k_base.bpe_ranks, + cl100k_base.special_tokens, + cl100k_base.pat_str, + ); + } + const tokens = enc.encode(text); + enc.free(); + return tokens.length; + }, +}; + +module.exports = { tokens }; + +// tokens.count({text, model}) => integer diff --git a/lib/openai/utils/tools.js b/lib/openai/utils/tools.js new file mode 100644 index 0000000..6b0158f --- /dev/null +++ b/lib/openai/utils/tools.js @@ -0,0 +1,40 @@ +'use strict'; + +const getTool = ({ name, description, properties, required } = {}) => ({ + type: 'function', + function: { + name, + description, + parameters: { + type: 'object', + properties, + required, + }, + }, +}); + +const defineTools = (params = []) => { + const tools = new Array(params.length).fill(Object.create(null)); + const functions = {}; + + for (let i = 0; i < params.length; i++) { + const f = params[i]; + const name = + f.name || + f.fn.name.replace(/([A-Z])/g, ($0, $1) => '_' + $1.toLowerCase()); + tools[i] = getTool({ ...f, name }); + const { fn, scope } = f; + functions[name] = { fn, scope }; + // if (tool.type === 'code_interpreter'){ + // functions.push({ + // "type": "code_interpreter", + // "code": tool.code + // }); + // } + } + return { tools, functions }; +}; + +module.exports = { + defineTools, +}; diff --git a/package-lock.json b/package-lock.json index 4ecf50d..292218d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,812 +1,376 @@ { - "name": "universal-modules", - "version": "1.0.0", + "name": "universal-llm", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "universal-modules", - "version": "1.0.0", + "name": "universal-llm", + "version": "0.0.1", "license": "MIT", "dependencies": { "@dip1059/safe-math-js": "^1.0.0", - "@fastify/cors": "^7.0.0", - "@fastify/static": "^7.0.4", - "@huggingface/agents": "^0.0.5", - "@huggingface/hub": "^0.14.2", - "@huggingface/inference": "^2.6.6", - "@sendgrid/mail": "^8.1.1", - "auto-bind": "^5.0.1", - "dotenv": "^16.4.5", + "@huggingface/inference": "^2.6.7", "elevenlabs-node": "^2.0.3", - "fastify": "^4.27.0", - "fastify-cors": "^6.1.0", "fluent-ffmpeg": "^2.1.2", "from-exponential": "^1.1.1", - "ioredis": "^5.3.2", - "js-tiktoken": "^1.0.10", - "lodash": "^4.17.21", "music-metadata": "^7.14.0", "ollama": "^0.5.0", - "openai": "^4.32.1", - "pg": "^8.11.3", - "redis": "^4.6.13", + "openai": "^4.41.1", "sharp": "^0.33.3", - "tiktoken": "^1.0.13" + "tiktoken": "^1.0.14" }, "devDependencies": { - "jest": "^29.7.0", - "jest-extended": "^4.0.2" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "eslint": "^8.57.0", + "prettier": "^3.2.5", + "typescript": "^5.3.3" }, "engines": { - "node": ">=6.0.0" + "node": "18 || 20 || 21 || 22" } }, - "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@dip1059/safe-math-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@dip1059/safe-math-js/-/safe-math-js-1.0.0.tgz", + "integrity": "sha512-p8DR7IKguaRZCBZa1MzZI+XWDNf7DaYyzr1nQq7jCcbgIJVfiU30OZOrUVRS3DAjSKbjZyyKSDHNMCx5kgEGjw==" }, - "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" + "node_modules/@emnapi/runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz", + "integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@babel/core": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", - "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.24.5", - "@babel/helpers": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@babel/generator": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", - "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, - "dependencies": { - "@babel/types": "^7.24.5", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, "engines": { - "node": ">=6.9.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, + "node_modules/@huggingface/inference": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-2.6.7.tgz", + "integrity": "sha512-vFBqvtU3LhxjufTs0jcRrDSc0nK+lah10bOgvlIn80lAH4JwMzHHPBQ4g4ECEdRD0PIt6EpTiidEZQq2sArb5Q==", "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.0" + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { - "node": ">=6.9.0" + "node": ">=10.10.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", - "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.24.3", - "@babel/helper-simple-access": "^7.24.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/helper-validator-identifier": "^7.24.5" - }, "engines": { - "node": ">=6.9.0" + "node": ">=12.22" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", - "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", - "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.5" - }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.3.tgz", + "integrity": "sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", - "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.5" + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.2" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", - "dev": true, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.3.tgz", + "integrity": "sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.2" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", - "dev": true, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", + "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", - "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5" + "macos": ">=10.13", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", + "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/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==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@babel/highlight/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==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", + "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/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==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@babel/highlight/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==", - "dev": true - }, - "node_modules/@babel/highlight/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==", - "dev": true, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", + "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.8.0" + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@babel/highlight/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==", - "dev": true, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", + "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/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==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@babel/parser": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", - "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", - "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", - "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", - "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/types": "^7.24.5", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", - "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.24.1", - "@babel/helper-validator-identifier": "^7.24.5", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@dip1059/safe-math-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@dip1059/safe-math-js/-/safe-math-js-1.0.0.tgz", - "integrity": "sha512-p8DR7IKguaRZCBZa1MzZI+XWDNf7DaYyzr1nQq7jCcbgIJVfiU30OZOrUVRS3DAjSKbjZyyKSDHNMCx5kgEGjw==" - }, - "node_modules/@emnapi/runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz", - "integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==", + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", + "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", + "cpu": [ + "arm64" + ], "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@fastify/accept-negotiator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz", - "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@fastify/ajv-compiler": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", - "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", - "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" - } - }, - "node_modules/@fastify/cors": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-7.0.0.tgz", - "integrity": "sha512-nlo6ScwagBNJacAZD3KX90xjWLIoV0vN9QqoX1wUE9ZeZMdvkVkMZCGlxEtr00NshV0X5wDge4w5rwox7rRzSg==", - "dependencies": { - "fastify-plugin": "^3.0.0", - "vary": "^1.1.2" - } - }, - "node_modules/@fastify/error": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", - "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" - }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", - "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", - "dependencies": { - "fast-json-stringify": "^5.7.0" - } - }, - "node_modules/@fastify/merge-json-schemas": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", - "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, - "node_modules/@fastify/send": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", - "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", - "dependencies": { - "@lukeed/ms": "^2.0.1", - "escape-html": "~1.0.3", - "fast-decode-uri-component": "^1.0.1", - "http-errors": "2.0.0", - "mime": "^3.0.0" - } - }, - "node_modules/@fastify/static": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.4.tgz", - "integrity": "sha512-p2uKtaf8BMOZWLs6wu+Ihg7bWNBdjNgCwDza4MJtTqg+5ovKmcbgbR9Xs5/smZ1YISfzKOCNYmZV8LaCj+eJ1Q==", - "dependencies": { - "@fastify/accept-negotiator": "^1.0.0", - "@fastify/send": "^2.0.0", - "content-disposition": "^0.5.3", - "fastify-plugin": "^4.0.0", - "fastq": "^1.17.0", - "glob": "^10.3.4" - } - }, - "node_modules/@fastify/static/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@fastify/static/node_modules/fastify-plugin": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", - "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" - }, - "node_modules/@fastify/static/node_modules/glob": { - "version": "10.3.14", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.14.tgz", - "integrity": "sha512-4fkAqu93xe9Mk7le9v0y3VrPDqLKHarNi2s4Pv7f2yOvfhWfhc7hRPHC/JyqMqb8B/Dt/eGS4n7ykwf3fOsl8g==", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.11.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@fastify/static/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=16 || 14 >=14.17" + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@huggingface/agents": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@huggingface/agents/-/agents-0.0.5.tgz", - "integrity": "sha512-h3mutXX0sFg4o9ZDHh3gzsF+qydcrTW9MVll2g/IVrNiQ0pSfXZqOQPRnfd85XU49buY3Zd1oHUDI0Bz8PznJw==", - "dependencies": { - "@huggingface/inference": "^2.6.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@huggingface/hub": { - "version": "0.14.11", - "resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-0.14.11.tgz", - "integrity": "sha512-/bcDxXLvtcMp0DA/w42tYyNcivusORLBiFGYPpZdpmgCyK6qD0D8texNE2NFmPeHPqwqOBjV6pGpZ1i4o6B/1A==", - "dependencies": { - "hash-wasm": "^4.9.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@huggingface/inference": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-2.6.7.tgz", - "integrity": "sha512-vFBqvtU3LhxjufTs0jcRrDSc0nK+lah10bOgvlIn80lAH4JwMzHHPBQ4g4ECEdRD0PIt6EpTiidEZQq2sArb5Q==", - "engines": { - "node": ">=18" + "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.3.tgz", - "integrity": "sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==", + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", + "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", "cpu": [ - "arm64" + "x64" ], "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "musl": ">=1.2.2", "npm": ">=9.6.5", "pnpm": ">=7.1.0", "yarn": ">=3.2.0" }, "funding": { "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.2" } }, - "node_modules/@img/sharp-darwin-x64": { + "node_modules/@img/sharp-linux-arm": { "version": "0.33.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.3.tgz", - "integrity": "sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.3.tgz", + "integrity": "sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==", "cpu": [ - "x64" + "arm" ], "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "glibc": ">=2.26", + "glibc": ">=2.28", "node": "^18.17.0 || ^20.3.0 || >=21.0.0", "npm": ">=9.6.5", "pnpm": ">=7.1.0", @@ -816,216 +380,23 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.2" + "@img/sharp-libvips-linux-arm": "1.0.2" } }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", - "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.3.tgz", + "integrity": "sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==", "cpu": [ "arm64" ], "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "macos": ">=11", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", - "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "macos": ">=10.13", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", - "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", - "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", - "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", - "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", - "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", - "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.3.tgz", - "integrity": "sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.2" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.3.tgz", - "integrity": "sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", "npm": ">=9.6.5", "pnpm": ">=7.1.0", "yarn": ">=3.2.0" @@ -1139,3078 +510,984 @@ }, "node_modules/@img/sharp-wasm32": { "version": "0.33.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.3.tgz", - "integrity": "sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==", - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.1.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.3.tgz", - "integrity": "sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.3.tgz", - "integrity": "sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@lukeed/ms": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", - "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/client": { - "version": "1.5.14", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.14.tgz", - "integrity": "sha512-YGn0GqsRBFUQxklhY7v562VMOP0DcmlrHHs3IV1mFE3cbxe31IITUkqhBcIhVSI/2JqtWAJXg5mjV4aU+zD0HA==", - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/client/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@redis/graph": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", - "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", - "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", - "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", - "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@sendgrid/client": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.3.tgz", - "integrity": "sha512-mRwTticRZIdUTsnyzvlK6dMu3jni9ci9J+dW/6fMMFpGRAJdCJlivFVYQvqk8kRS3RnFzS7sf6BSmhLl1ldDhA==", - "dependencies": { - "@sendgrid/helpers": "^8.0.0", - "axios": "^1.6.8" - }, - "engines": { - "node": ">=12.*" - } - }, - "node_modules/@sendgrid/helpers": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", - "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", - "dependencies": { - "deepmerge": "^4.2.2" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/@sendgrid/mail": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.3.tgz", - "integrity": "sha512-Wg5iKSUOER83/cfY6rbPa+o3ChnYzWwv1OcsR8gCV8SKi+sUPIMroildimlnb72DBkQxcbylxng1W7f0RIX7MQ==", - "dependencies": { - "@sendgrid/client": "^8.1.3", - "@sendgrid/helpers": "^8.0.0" - }, - "engines": { - "node": ">=12.*" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "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/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/node": { - "version": "20.12.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", - "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" - }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" - }, - "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/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/auto-bind": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", - "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/avvio": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.0.tgz", - "integrity": "sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==", - "dependencies": { - "@fastify/error": "^3.3.0", - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.17.1" - } - }, - "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "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/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001617", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz", - "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "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" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "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/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "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/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.762", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.762.tgz", - "integrity": "sha512-rrFvGweLxPwwSwJOjIopy3Vr+J3cIPtZzuc74bmlvmBIgQO3VYJDvVrlj94iKZ3ukXUH64Ex31hSfRTLqvjYJQ==", - "dev": true - }, - "node_modules/elevenlabs-node": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/elevenlabs-node/-/elevenlabs-node-2.0.3.tgz", - "integrity": "sha512-BB6JopTDT+dJ/yNMhqPBzu2yz5Pooos86c8/CFZT6PRnx2in9wh3z560ae/6qpv5XRbpNOOhi/aC51CTdYJr5A==", - "dependencies": { - "axios": "^1.4.0", - "fs-extra": "^11.1.1" - } - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "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": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-content-type-parse": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", - "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-json-stringify": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.15.1.tgz", - "integrity": "sha512-JopGtkvvguRqrS4gHXSSA2jf4pDgOZkeBAkLO1LbzOpiOMo7/kugoR+KiWifpLpluaVeYDkAuxCJOj4Gyc6L9A==", - "dependencies": { - "@fastify/merge-json-schemas": "^0.1.0", - "ajv": "^8.10.0", - "ajv-formats": "^3.0.1", - "fast-deep-equal": "^3.1.3", - "fast-uri": "^2.1.0", - "json-schema-ref-resolver": "^1.0.1", - "rfdc": "^1.2.0" - } - }, - "node_modules/fast-json-stringify/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-uri": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", - "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==" - }, - "node_modules/fastify": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.27.0.tgz", - "integrity": "sha512-ci9IXzbigB8dyi0mSy3faa3Bsj0xWAPb9JeT4KRzubdSb6pNhcADRUaXCBml6V1Ss/a05kbtQls5LBmhHydoTA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "dependencies": { - "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.4.0", - "@fastify/fast-json-stringify-compiler": "^4.3.0", - "abstract-logging": "^2.0.1", - "avvio": "^8.3.0", - "fast-content-type-parse": "^1.1.0", - "fast-json-stringify": "^5.8.0", - "find-my-way": "^8.0.0", - "light-my-request": "^5.11.0", - "pino": "^9.0.0", - "process-warning": "^3.0.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.7.0", - "semver": "^7.5.4", - "toad-cache": "^3.3.0" - } - }, - "node_modules/fastify-cors": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/fastify-cors/-/fastify-cors-6.1.0.tgz", - "integrity": "sha512-QBKz32IoY/iuT74CunRY1XOSpjSTIOh9E3FxulXIBhd0D2vdgG0kDvy0eG6HA/88sRfWHeba43LkGEXPz0Rh8g==", - "deprecated": "Please use @fastify/cors@7.0.0 instead", - "dependencies": { - "fastify-cors-deprecated": "npm:fastify-cors@6.0.3", - "process-warning": "^1.0.0" - } - }, - "node_modules/fastify-cors-deprecated": { - "name": "fastify-cors", - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/fastify-cors/-/fastify-cors-6.0.3.tgz", - "integrity": "sha512-fMbXubKKyBHHCfSBtsCi3+7VyVRdhJQmGes5gM+eGKkRErCdm0NaYO0ozd31BQBL1ycoTIjbqOZhJo4RTF/Vlg==", - "dependencies": { - "fastify-plugin": "^3.0.0", - "vary": "^1.1.2" - } - }, - "node_modules/fastify-cors/node_modules/process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" - }, - "node_modules/fastify-plugin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.1.tgz", - "integrity": "sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA==" - }, - "node_modules/fastify/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-my-way": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.0.tgz", - "integrity": "sha512-HdWXgFYc6b1BJcOBDBwjqWuHJj1WYiqrxSh25qtU4DabpMFdj/gSunNBQb83t+8Zt67D7CXEzJWTkxaShMTMOA==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^3.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fluent-ffmpeg": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz", - "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==", - "dependencies": { - "async": ">=0.2.9", - "which": "^1.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/formdata-node/node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from-exponential": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/from-exponential/-/from-exponential-1.1.1.tgz", - "integrity": "sha512-VBE7f5OVnYwdgB3LHa+Qo29h8qVpxhVO9Trlc+AWm+/XNAgks1tAwMFHb33mjeiof77GglsJzeYF7OqXrROP/A==" - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.3.tgz", + "integrity": "sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==", + "cpu": [ + "wasm32" + ], + "optional": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@emnapi/runtime": "^1.1.0" }, "engines": { - "node": ">=14.14" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.3.tgz", + "integrity": "sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==", + "cpu": [ + "ia32" + ], "optional": true, "os": [ - "darwin" + "win32" ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/libvips" } }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.3.tgz", + "integrity": "sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 4" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">=6.9.0" + "node": ">= 8" } }, - "node_modules/get-caller-file": { + "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 8" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, "engines": { - "node": ">=8.0.0" + "node": ">= 8" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "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/node": { + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", + "dependencies": { + "undici-types": "~5.26.4" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=6.5" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=4" + "node": ">=0.4.0" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "engines": { - "node": ">=8" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/hash-wasm": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.11.0.tgz", - "integrity": "sha512-HVusNXlVqHe0fzIzdQOGolnFN6mX/fqcrSAOcTBXdvzrXVHwTz11vXeKRmkR5gTuwVpvHZEIyKoePDvuAR+XwQ==" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", "dependencies": { - "function-bind": "^1.1.2" + "humanize-ms": "^1.2.1" }, "engines": { - "node": ">= 0.4" + "node": ">= 8.0.0" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">= 0.8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": { - "ms": "^2.0.0" + "node": ">=8" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "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/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "node_modules/ioredis": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", - "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12.22.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, "engines": { - "node": ">= 0.10" + "node": ">=12.5.0" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "hasown": "^2.0.0" + "color-name": "~1.1.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { - "node": ">=8" + "node": ">=7.0.0" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.8" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">= 8" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" }, "engines": { - "node": ">=10" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "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": ">=10" + "node": ">=0.4.0" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "node_modules/elevenlabs-node": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/elevenlabs-node/-/elevenlabs-node-2.0.3.tgz", + "integrity": "sha512-BB6JopTDT+dJ/yNMhqPBzu2yz5Pooos86c8/CFZT6PRnx2in9wh3z560ae/6qpv5XRbpNOOhi/aC51CTdYJr5A==", "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, + "axios": "^1.4.0", + "fs-extra": "^11.1.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { - "jest": "bin/jest.js" + "eslint": "bin/eslint.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "estraverse": "^5.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=0.10" } }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "estraverse": "^5.2.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4.0" } }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4.0" } }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/jest-extended": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.2.tgz", - "integrity": "sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { - "jest-diff": "^29.0.0", - "jest-get-type": "^29.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "jest": ">=27.2.5" - }, - "peerDependenciesMeta": { - "jest": { - "optional": true - } + "reusify": "^1.0.4" } }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/fluent-ffmpeg": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz", + "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "async": ">=0.2.9", + "which": "^1.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.8.0" } }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, + "node_modules/fluent-ffmpeg/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" + "isexe": "^2.0.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "which": "bin/which" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" + "node": ">=4.0" }, "peerDependenciesMeta": { - "jest-resolve": { + "debug": { "optional": true } } }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": 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": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 12.20" } }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/from-exponential": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/from-exponential/-/from-exponential-1.1.1.tgz", + "integrity": "sha512-VBE7f5OVnYwdgB3LHa+Qo29h8qVpxhVO9Trlc+AWm+/XNAgks1tAwMFHb33mjeiof77GglsJzeYF7OqXrROP/A==" + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=14.14" } }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "is-glob": "^4.0.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10.13.0" } }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" + "type-fest": "^0.20.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "bin": { - "semver": "bin/semver.js" - }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "ms": "^2.0.0" } }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 4" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.8.19" } }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/js-tiktoken": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.11.tgz", - "integrity": "sha512-PajXFLq2vx7/8jllQZ43vzNpAai/0MOVdJjW/UrNyJorNQRTjHrqdGJG/mjHVy7h9M6dW6CaG43eNLMYFkTh6w==", - "dependencies": { - "base64-js": "^1.5.1" + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/json-schema-ref-resolver": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", - "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "node_modules/jsonfile": { "version": "6.1.0", @@ -4223,83 +1500,35 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/light-my-request": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.13.0.tgz", - "integrity": "sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==", "dependencies": { - "cookie": "^0.6.0", - "process-warning": "^3.0.0", - "set-cookie-parser": "^2.4.1" + "json-buffer": "3.0.1" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" + "node": ">= 0.8.0" } }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "semver": "^7.5.3" + "p-locate": "^5.0.0" }, "engines": { "node": ">=10" @@ -4308,27 +1537,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -4337,36 +1562,6 @@ "node": ">= 0.8" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4386,15 +1581,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4407,14 +1593,6 @@ "node": "*" } }, - "node_modules/minipass": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4461,531 +1639,184 @@ "url": "https://paypal.me/jimmywarting" } ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ollama": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.5.0.tgz", - "integrity": "sha512-CRtRzsho210EGdK52GrUMohA2pU+7NbgEaBG3DcYeRmvQthDO7E2LHOkLlUUeaYUlNmEd8icbjC02ug9meSYnw==", - "dependencies": { - "whatwg-fetch": "^3.6.20" - } - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openai": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.44.0.tgz", - "integrity": "sha512-jVpDIJsBAR83rVbIHPuWRr9UkFc5DaH9ev2kt2IQAhKCs73DBRoFOa5SwtqfN7/CcBdIGBdygpmpc0gsFaV+Ow==", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "web-streams-polyfill": "^3.2.1" - }, - "bin": { - "openai": "bin/cli" - } - }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", - "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.0.tgz", - "integrity": "sha512-LNHTaVkzaYaLGlO+0u3rQTz7QrHTFOuKyba9JMTQutkmtNew8dw8wOD7mTU/5fCPZzCWpfW0XnQKzY61P0aTaw==", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "engines": { + "node": ">=10.5.0" } }, - "node_modules/pg": { - "version": "8.11.5", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", - "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dependencies": { - "pg-connection-string": "^2.6.4", - "pg-pool": "^3.6.2", - "pg-protocol": "^1.6.1", - "pg-types": "^2.1.0", - "pgpass": "1.x" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">= 8.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.1.1" + "node": "4.x || >=6.0.0" }, "peerDependencies": { - "pg-native": ">=3.0.1" + "encoding": "^0.1.0" }, "peerDependenciesMeta": { - "pg-native": { + "encoding": { "optional": true } } }, - "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", - "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", - "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", - "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "node_modules/ollama": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.5.0.tgz", + "integrity": "sha512-CRtRzsho210EGdK52GrUMohA2pU+7NbgEaBG3DcYeRmvQthDO7E2LHOkLlUUeaYUlNmEd8icbjC02ug9meSYnw==", "dependencies": { - "split2": "^4.1.0" + "whatwg-fetch": "^3.6.20" } }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "dependencies": { + "wrappy": "1" } }, - "node_modules/pino": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.0.0.tgz", - "integrity": "sha512-uI1ThkzTShNSwvsUM6b4ND8ANzWURk9zTELMztFkmnCQeR/4wkomJ+echHee5GMWGovoSfjwdeu80DsFIt7mbA==", + "node_modules/openai": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.41.1.tgz", + "integrity": "sha512-QY8pkCOTjwXWS1AsTxRLomkX6B9fBQvggqRu4uvFd7Xjg1NDcPChGyki+sFiYzstpUeoApim/DNGy7fBKwdokg==", "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.2.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^3.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.7.0", - "thread-stream": "^2.6.0" + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" }, "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", - "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", - "dependencies": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" + "openai": "bin/cli" } }, - "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.8.0" } }, - "node_modules/pino-std-serializers": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", - "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "find-up": "^4.0.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dependencies": { - "xtend": "^4.0.0" - }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "engines": { - "node": ">= 0.6.0" + "node": ">= 0.8.0" } }, - "node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "node": ">=14" }, - "engines": { - "node": ">= 0.10" + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/proxy-from-env": { @@ -4997,37 +1828,31 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "engines": { "node": ">=6" } }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" }, { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" + "type": "consulting", + "url": "https://feross.org/support" } ] }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -5056,139 +1881,45 @@ "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/redis": { - "version": "4.6.13", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.13.tgz", - "integrity": "sha512-MHgkS4B+sPjCXpf+HfdetBwbRz6vCtsceTmw1pHNYJAsYxrfpOP6dz+piJWGos8wqG7qb3vj/Rrc5qOlmInUuA==", - "workspaces": [ - "./packages/*" - ], - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.5.14", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.6", - "@redis/search": "1.1.6", - "@redis/time-series": "1.0.5" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "dependencies": { - "redis-errors": "^1.0.0" - }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "engines": { + "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "glob": "^7.1.3" }, "bin": { - "resolve": "bin/resolve" + "rimraf": "bin.js" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ret": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", - "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" - }, - "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", @@ -5202,48 +1933,44 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/safe-regex2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", - "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", + ], "dependencies": { - "ret": "~0.4.0" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" + "queue-microtask": "^1.2.2" } }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" + "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/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/set-cookie-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, "node_modules/sharp": { "version": "0.33.3", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.3.tgz", @@ -5283,21 +2010,11 @@ "@img/sharp-win32-x64": "0.33.3" } }, - "node_modules/sharp/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -5309,16 +2026,11 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -5327,92 +2039,6 @@ "is-arrayish": "^0.3.1" } }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sonic-boom": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", - "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -5421,50 +2047,11 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5472,36 +2059,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5542,88 +2099,17 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/thread-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", - "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", - "dependencies": { - "real-require": "^0.2.0" - } + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/tiktoken": { "version": "1.0.14", "resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.14.tgz", "integrity": "sha512-g5zd5r/DoH8Kw0fiYbYpVhb6WO8BHO1unXqmBBWKwoT17HwSounnDtMDFUKm2Pko8U47sjQarOe+9aUrnqmmTg==" }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toad-cache": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", - "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/token-types": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", @@ -5651,19 +2137,22 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "optional": true }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, "engines": { - "node": ">=4" + "node": ">= 0.8.0" } }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { "node": ">=10" @@ -5672,6 +2161,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -5685,40 +2187,11 @@ "node": ">= 10.0.0" } }, - "node_modules/update-browserslist-db": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz", - "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -5728,37 +2201,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -5787,48 +2229,27 @@ } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "node-which": "bin/node-which" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">= 8" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=0.10.0" } }, "node_modules/wrappy": { @@ -5837,68 +2258,10 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yocto-queue": { "version": "0.1.0", diff --git a/package.json b/package.json index cae566f..43394fb 100644 --- a/package.json +++ b/package.json @@ -1,52 +1,51 @@ { - "name": "universal-modules", - "version": "1.0.0", - "description": "Universal modules for rapid node.js application development", - "main": "index.js", - "scripts": { - "test": "jest" + "name": "universal-llm", + "version": "0.0.1", + "description": "Powerful, configurable and usable Universal LLM modules for all your AI needs.", + "main": "lib/index.js", + "types": "types.d.ts", + "engines": { + "node": "18 || 20 || 21 || 22" }, - "repository": { - "type": "git", - "url": "git+https://github.com/leonpolak/universal.git" + "readmeFilename": "README.md", + "scripts": { + "test": "npm run lint && npm run types && node --env-file=.env --test --test-timeout 60000", + "types": "tsc -p tsconfig.json", + "lint": "eslint . && prettier --check \"**/*.js\" \"**/*.json\" \"**/*.md\" \"**/.*rc\"", + "fmt": "prettier --write \"**/*.js\" \"**/*.json\" \"**/*.md\" \"**/.*rc\"" }, "keywords": [ - "modules" + "AI", + "LLM", + "GPT", + "Deep Learning", + "Fine-tuning", + "Text Generation", + "Speech Recognition", + "Image Generation" ], - "author": "Collective", + "author": "Leon Polak ", "license": "MIT", "bugs": { - "url": "https://github.com/leonpolak/universal/issues" + "url": "https://github.com/leonpolak/universal-llm/issues", + "email": "lp0808@gmail.com" + }, + "homepage": "https://github.com/leonpolak/universal-llm#readme", + "devDependencies": { + "eslint": "^8.57.0", + "prettier": "^3.2.5", + "typescript": "^5.3.3" }, - "homepage": "https://github.com/leonpolak/universal#readme", "dependencies": { "@dip1059/safe-math-js": "^1.0.0", - "@fastify/cors": "^7.0.0", - "@fastify/static": "^7.0.4", - "@huggingface/agents": "^0.0.5", - "@huggingface/hub": "^0.14.2", - "@huggingface/inference": "^2.6.6", - "@sendgrid/mail": "^8.1.1", - "auto-bind": "^5.0.1", - "dotenv": "^16.4.5", + "@huggingface/inference": "^2.6.7", "elevenlabs-node": "^2.0.3", - "fastify": "^4.27.0", - "fastify-cors": "^6.1.0", "fluent-ffmpeg": "^2.1.2", "from-exponential": "^1.1.1", - "ioredis": "^5.3.2", - "js-tiktoken": "^1.0.10", - "lodash": "^4.17.21", "music-metadata": "^7.14.0", "ollama": "^0.5.0", - "openai": "^4.32.1", - "pg": "^8.11.3", - "redis": "^4.6.13", + "openai": "^4.41.1", "sharp": "^0.33.3", - "tiktoken": "^1.0.13" - }, - "devDependencies": { - "jest": "^29.7.0", - "jest-extended": "^4.0.2" + "tiktoken": "^1.0.14" } } diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 91b6b97..0000000 --- a/public/index.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - Chat with AI - - - - -

-

Chat with AI

- - -
- -
-
- - - - - diff --git a/server.js b/server.js deleted file mode 100644 index 4e075e4..0000000 --- a/server.js +++ /dev/null @@ -1,44 +0,0 @@ -const path = require('path') -const fs = require('fs') -const openAI = require('./OpenAI/openai-connector') - -const fastify = require('fastify')({ - logger: true, - http2: true, - https: { - allowHTTP1: true, // fallback support for HTTP1 - key: fs.readFileSync(path.join(__dirname, '.cert', 'key.pem')), - cert: fs.readFileSync(path.join(__dirname, '.cert', 'cert.pem')) - } - }) -// fastify.register(require('@fastify/cookie')); -// fastify.register(require('@fastify/session'), {secret: 'a secret with minimum length of 32 characters'}); - - // Register CORS plugin (if your frontend is served from a different origin) -// fastify.register(require('@fastify/cors@7.0.0'), { -// // origin: "*", -// // Configure CORS if necessary -// }); - fastify.register(require('@fastify/static'), { - root: path.join(__dirname, 'public'), - prefix: '/', // optional: default '/' - // constraints: { host: 'example.com' } // optional: default {} -}) - - // Route to handle POST requests - fastify.post('/sendMessage', async (request, reply) => { - const userMessage = request.body.message; - const response = await openAI.language.generate({text:userMessage}); - - return { reply: response.message }; - }); - - // Run the server! - fastify.listen({ port: 3000 }, function (err, address) { - if (err) { - fastify.log.error(err) - process.exit(1) - } - // Server is now listening on ${address} - }) - \ No newline at end of file diff --git a/test/elevenlabs.js b/test/elevenlabs.js new file mode 100644 index 0000000..49eaaee --- /dev/null +++ b/test/elevenlabs.js @@ -0,0 +1,109 @@ +'use strict'; + +const test = require('node:test'); +const assert = require('node:assert'); +const { elevenlabs } = require('../lib'); +const { + textToSpeech, + getVoices, + getVoice, + getModels, + getUserInfo, + getUserSubscription, + getDefaultVoiceSettings, + // textToSpeechStream, + // editVoiceSettings, + // getVoiceSettings, + // deleteVoice, +} = elevenlabs; + +test('Elevenlabs Connector', async (t) => { + await t.test('textToSpeech', async () => { + const res = await textToSpeech('hello'); + + assert.ok(typeof res === 'object'); + assert.ok('status' in res); + assert.strictEqual(res.status, 'ok'); + }); + + await t.test('getVoices', async () => { + const res = await getVoices(); + + assert.ok(typeof res === 'object'); + assert.ok('voices' in res); + assert.ok(Array.isArray(res.voices)); + }); + + await t.test('getVoice', async () => { + const res = await getVoice(); + + assert.ok(typeof res === 'object'); + assert.ok('voice_id' in res); + assert.ok('name' in res); + assert.ok('labels' in res); + assert.ok('gender' in res.labels); + assert.ok('accent' in res.labels); + }); + + await t.test('getModels', async () => { + const res = await getModels(); + + assert.ok(Array.isArray(res)); + + for (const model of res) { + assert.ok(typeof model === 'object'); + assert.ok('name' in model); + assert.ok('model_id' in model); + assert.ok('description' in model); + } + }); + + await t.test('getUserInfo', async () => { + const res = await getUserInfo(); + + assert.ok(typeof res === 'object'); + assert.ok('subscription' in res); + assert.ok('first_name' in res); + }); + + await t.test('getUserSubscription', async () => { + const res = await getUserSubscription(); + + assert.ok(typeof res === 'object'); + assert.ok('tier' in res); + assert.ok('status' in res); + }); + + await t.test('getDefaultVoiceSettings', async () => { + const res = await getDefaultVoiceSettings(); + + assert.ok(typeof res === 'object'); + assert.ok('stability' in res); + }); + + //WARN : Return undefined + + /* await t.test('editVoiceSettings', async () => { + const res = await editVoiceSettings(); + + assert.ok(Array.isArray(res)); + }); + + await t.test('getVoiceSettings', async () => { + const res = await getVoiceSettings(); + console.log({ res }); + assert.ok(Array.isArray(res)); + }); + + await t.test('textToSpeechStream', async () => { + const res = await textToSpeechStream(''); + + assert.ok(Array.isArray(res)); + }); + + await t.test('deleteVoice', async () => { + const res = await deleteVoice(); + + assert.ok(Array.isArray(res)); + }); */ +}); diff --git a/test/huggingface.js b/test/huggingface.js new file mode 100644 index 0000000..921ecd7 --- /dev/null +++ b/test/huggingface.js @@ -0,0 +1,439 @@ +'use strict'; + +const test = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); +const { readFileSync } = require('node:fs'); +const { huggingface } = require('../lib'); + +const { + FillMask, + Summarization, + QuestionAnswering, + TableQuestionAnswering, + TextClassification, + TextGeneration, + TextGenerationStream, + TokenClassification, + Translation, + ZeroShotClassification, + SentenceSimilarity, + + AutomaticSpeechRecognition, + AudioClassification, + TextToSpeech, + // AudioToAudio, + + ImageClassification, + ObjectDetection, + ImageSegmentation, + ImageToText, + TextToImage, + ImageToImage, + ZeroShotImageClassification, + FeatureExtraction, + + VisualQuestionAnswering, + DocumentQuestionAnswering, + // TabularRegression, + // TabularClassification, + // CustomCall, + // CustomInferenceEndpoint, + CustomCallStreaming, +} = huggingface; + +const FILES = process.cwd() + '/files/huggingface/'; +const AUDIOS = FILES + 'audios'; +const IMAGES = FILES + 'images'; + +const testAudioFile = readFileSync(path.join(AUDIOS, 'speech.mp3')); +const testCatFile = readFileSync(path.join(IMAGES, 'cat.jpg')); +const testSeeFile = readFileSync(path.join(IMAGES, 'see.jpeg')); +const testInvoiceFile = readFileSync(path.join(IMAGES, 'invoice.png')); + +test('HuggingFace Connector', async (t) => { + await t.test('FillMask', async () => { + const masks = await FillMask('[MASK] world!'); + + assert.ok(Array.isArray(masks)); + + for (const mask of masks) { + assert.ok(typeof mask === 'object'); + assert.ok('score' in mask); + assert.ok('sequence' in mask); + assert.ok('token' in mask); + assert.ok('token_str' in mask); + } + }); + + await t.test('Summarization', async () => { + const res = await Summarization( + 'The tower is 324 metres (1,063 ft) tall about the same height as an' + + '81-storey building, and the tallest structure in Paris. Its base is' + + 'square, measuring 125 metres (410 ft) on each side. During its' + + 'construction, the Eiffel Tower surpassed the Washington Monument to' + + 'become the tallest', + ); + + assert.ok(typeof res === 'object'); + assert.ok('summary_text' in res); + }); + + await t.test('QuestionAnswering', async () => { + const res = await QuestionAnswering({ + question: 'What is the capital of France?', + context: 'The capital of France is Paris.', + }); + + assert.ok(typeof res === 'object'); + assert.ok('score' in res); + assert.ok('start' in res); + assert.ok('end' in res); + assert.ok('answer' in res); + assert.strictEqual(res.answer, 'Paris'); + }); + + await t.test('TableQuestionAnswering', async () => { + const res = await TableQuestionAnswering({ + query: 'How many stars does the transformers repository have?', + table: { + Repository: ['Transformers', 'Datasets', 'Tokenizers'], + Stars: ['36542', '4512', '3934'], + Contributors: ['651', '77', '34'], + 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'], + }, + }); + + assert.ok(typeof res === 'object'); + assert.ok('answer' in res); + assert.ok('coordinates' in res); + assert.ok('cells' in res); + assert.ok('aggregator' in res); + assert.deepEqual(res.coordinates, [[0, 1]]); + assert.strictEqual(res.aggregator, 'AVERAGE'); + }); + + await t.test('TextClassification', async () => { + const res = await TextClassification('I like you. I love you.'); + + assert.ok(Array.isArray(res)); + for (const item of res) { + assert.ok('label' in item); + assert.ok('score' in item); + } + }); + + await t.test('TextGeneration', async () => { + const res = await TextGeneration('The answer to the universe is'); + + assert.ok(typeof res === 'object'); + assert.ok('generated_text' in res); + }); + + await t.test('TextGenerationStream', async () => { + const res = await TextGenerationStream('repeat "one two three four"', { + max_new_tokens: 250, + }); + + assert.ok(typeof res === 'object'); + }); + + await t.test('TokenClassification', async () => { + const res = await TokenClassification( + 'My name is Sarah Jessica Parker but you can call me Jessica', + ); + + // console.log(res); + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('start' in item); + assert.ok('end' in item); + assert.ok('entity_group' in item); + assert.ok('score' in item); + assert.ok('word' in item); + } + }); + + await t.test('Translation', async () => { + const res = await Translation( + 'My name is Wolfgang and I live in Amsterdam', + { src_lang: 'en_XX', tgt_lang: 'fr_XX' }, + ); + + assert.ok(typeof res === 'object'); + assert.ok('translation_text' in res); + assert.ok(typeof res.translation_text === 'string'); + }); + + await t.test('ZeroShotClassification', async () => { + const res = await ZeroShotClassification( + [ + 'Hi, I recently bought a device from your company but' + + ' it is not working as advertised and I would like to' + + ' get reimbursed!', + ], + { + candidate_labels: ['refund', 'legal', 'faq'], + }, + ); + + assert.ok(Array.isArray(res)); + assert.ok(res.length === 1); + + const [item] = res; + + assert.ok(typeof item === 'object'); + assert.ok('sequence' in item); + assert.ok('labels' in item); + assert.ok('scores' in item); + assert.ok(typeof item.sequence === 'string'); + assert.ok(Array.isArray(item.labels)); + assert.ok(Array.isArray(item.scores)); + }); + + await t.test('SentenceSimilarity', async () => { + const res = await SentenceSimilarity({ + source_sentence: 'That is a happy person', + sentences: [ + 'That is a happy dog', + 'That is a very happy person', + 'Today is a sunny day', + ], + }); + + assert.ok(Array.isArray(res)); + assert.ok(res.every((item) => typeof item === 'number')); + }); + + await t.test('AutomaticSpeechRecognition', async () => { + const res = await AutomaticSpeechRecognition(testAudioFile); + + assert.ok(typeof res === 'object'); + assert.ok('text' in res); + assert.ok(typeof res.text === 'string'); + }); + + await t.test('AudioClassification', async () => { + const res = await AudioClassification(testAudioFile); + + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('label' in item); + assert.ok('score' in item); + } + }); + + await t.test('TextToSpeech', async () => { + const res = await TextToSpeech('Hello world!'); + + assert.ok(res instanceof Blob); + assert.ok(typeof res.size === 'number'); + assert.strictEqual(res.type, 'audio/flac'); + }); + + // TODO: fix test, getting an error "interface not in config.json" + // test.skip('AudioToAudio', async () => { + // const res = await AudioToAudio(testAudioFile); + // + // console.log(res); + // }); + + await t.test('ImageClassification', async () => { + const res = await ImageClassification(testCatFile); + + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('label' in item); + assert.ok('score' in item); + } + }); + + await t.test('ObjectDetection', async () => { + const res = await ObjectDetection(testCatFile); + + assert.ok(Array.isArray(res)); + assert.ok(res.length === 1); + + const [item] = res; + + assert.ok(typeof item === 'object'); + assert.ok('box' in item); + assert.ok('label' in item); + assert.ok('score' in item); + assert.ok(typeof item.box === 'object'); + assert.ok(typeof item.label === 'string'); + assert.ok(typeof item.score === 'number'); + assert.strictEqual(item.label, 'cat'); + + const { box } = item; + + assert.ok(typeof box === 'object'); + assert.ok(typeof box.xmin === 'number'); + assert.ok(typeof box.ymin === 'number'); + assert.ok(typeof box.xmax === 'number'); + assert.ok(typeof box.ymax === 'number'); + }); + + await t.test('ImageSegmentation', async () => { + const res = await ImageSegmentation(testCatFile); + + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('score' in item); + assert.ok('label' in item); + assert.ok('mask' in item); + } + }); + + await t.test('ImageToText', async () => { + const res = await ImageToText(new Blob([testSeeFile])); + + assert.ok(typeof res === 'object'); + assert.ok('generated_text' in res); + assert.ok(typeof res.generated_text === 'string'); + }); + + await t.test('TextToImage', async () => { + const inputs = + 'award winning high resolution photo of a giant tortoise' + + '/((ladybird)) hybrid, [trending on artstation]'; + const res = await TextToImage(inputs, { negative_prompt: 'blurry' }); + + assert.ok(res instanceof Blob); + assert.ok(typeof res.size === 'number'); + assert.ok(res.type === 'image/jpeg'); + }); + + await t.test('ImageToImage', async () => { + const res = await ImageToImage(new Blob([testSeeFile]), { + prompt: 'test picture', + }); + + assert.ok(res instanceof Blob); + assert.ok(typeof res.size === 'number'); + assert.ok(res.type === 'image/jpeg'); + }); + + await t.test('ZeroShotImageClassification', async () => { + const inputs = { image: new Blob([testCatFile]) }; + const res = await ZeroShotImageClassification(inputs, { + candidate_labels: ['cat', 'dog'], + }); + + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('score' in item); + assert.ok('label' in item); + } + }); + + await t.test('FeatureExtraction', async () => { + const res = await FeatureExtraction('That is a happy person'); + + assert.ok(Array.isArray(res)); + assert.ok(res.every((el) => typeof el === 'number')); + }); + + await t.test('VisualQuestionAnswering', async () => { + const inputs = { + question: 'How many cats are lying down?', + image: new Blob([testCatFile]), + }; + const res = await VisualQuestionAnswering(inputs); + + assert.ok(typeof res === 'object'); + assert.ok('score' in res); + assert.ok('answer' in res); + assert.ok(typeof res.score === 'number'); + assert.strictEqual(res.answer, '1'); + }); + + await t.test('DocumentQuestionAnswering', async () => { + const inputs = { + question: 'Invoice number?', + image: new Blob([testInvoiceFile]), + }; + const res = await DocumentQuestionAnswering(inputs); + + assert.ok(typeof res === 'object'); + assert.ok('score' in res && typeof res.score === 'number'); + assert.ok('start' in res && typeof res.start === 'number'); + assert.ok('end' in res && typeof res.end === 'number'); + assert.ok('answer' in res && typeof res.answer === 'string'); + assert.strictEqual(res.answer, 'us-001'); + }); + + // TODO: fix test, timeout + // test.skip('TabularRegression', async () => { + // const inputs = { + // data: { + // Height: ['11.52', '12.48', '12.3778'], + // Length1: ['23.2', '24', '23.9'], + // Length2: ['25.4', '26.3', '26.5'], + // Length3: ['30', '31.2', '31.1'], + // Species: ['Bream', 'Bream', 'Bream'], + // Width: ['4.02', '4.3056', '4.6961'], + // }, + // }; + // const res = await TabularRegression(inputs); + // + // console.log(res); + // }, 60000); + + // TODO: fix test, timeout + // test.skip('TabularClassification', async () => { + // const inputs = { + // data: { + // fixed_acidity: ['7.4', '7.8', '10.3'], + // volatile_acidity: ['0.7', '0.88', '0.32'], + // citric_acid: ['0', '0', '0.45'], + // residual_sugar: ['1.9', '2.6', '6.4'], + // chlorides: ['0.076', '0.098', '0.073'], + // free_sulfur_dioxide: ['11', '25', '5'], + // total_sulfur_dioxide: ['34', '67', '13'], + // density: ['0.9978', '0.9968', '0.9976'], + // pH: ['3.51', '3.2', '3.23'], + // sulphates: ['0.56', '0.68', '0.82'], + // alcohol: ['9.4', '9.8', '12.6'], + // }, + // }; + // const res = await TabularClassification(inputs); + // + // console.log(res); + // }, 60000); + + // TODO: fix test, response is undefined for some reason + // test.skip('CustomCall', async () => { + // const res = await CustomCall('hello world'); + // + // console.log(res); + // }); + + await t.test('CustomCallStreaming', async () => { + const res = await CustomCallStreaming('hello world'); + + assert.ok(typeof res === 'object'); + }); + + // TODO: To test this one we need to have own inference endpoint + // test.skip('CustomInferenceEndpoint', async () => { + // const endpoint = + // 'https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2'; + // const res = await CustomInferenceEndpoint( + // 'The answer to the universe is', + // endpoint, + // ); + // + // console.log(res); + // }); +}); diff --git a/test/ollama.js b/test/ollama.js new file mode 100644 index 0000000..c2f67c1 --- /dev/null +++ b/test/ollama.js @@ -0,0 +1,23 @@ +'use strict'; + +const test = require('node:test'); +const assert = require('node:assert'); +const { ollama } = require('../lib'); + +const { completion } = ollama; + +test('Ollama Connector', async (t) => { + await t.test('Completion', async () => { + const res = await completion([ + { + role: 'user', + content: 'how are you doing?', + }, + ]); + + assert.ok(typeof res === 'object'); + assert.ok('role' in res); + assert.ok('content' in res); + assert.strictEqual(res.role, 'assistant'); + }); +}); diff --git a/test/openai.js b/test/openai.js new file mode 100644 index 0000000..abe3919 --- /dev/null +++ b/test/openai.js @@ -0,0 +1,403 @@ +'use strict'; + +const test = require('node:test'); +const assert = require('node:assert'); +const fs = require('node:fs'); +const path = require('node:path'); +const { openai } = require('../lib'); +const utils = require('../lib/openai/utils'); +const common = require('../lib/common.js'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +const FILES = path.join(__dirname, '../files'); +const OPEN_AI_FILES = path.join(FILES, 'openai'); +const AUDIOS = path.join(OPEN_AI_FILES, 'audios'); +const IMAGES = path.join(OPEN_AI_FILES, 'images'); +const FINE_TUNE = path.join(OPEN_AI_FILES, 'fine-tune'); +const ASSISTANTS = path.join(OPEN_AI_FILES, 'assistants'); +const TOOLS = path.join(OPEN_AI_FILES, 'tools'); +const TEST_LIBRARY = require(TOOLS + '/test-library.js'); + +const { + language, + files, + fineTune, + models, + images, + speech, + recognition, + // tokens, + // assistants, +} = utils; + +const tested = { + chat: new Chat({}), + fineTune: { + id: '', + file: { id: '' }, + }, + model: { + id: '', + }, + assistant: { + id: '', + thread: { id: '' }, + message: { id: '' }, + run: { id: '' }, + step: { id: '' }, + file: { id: '' }, + added_file: { id: '' }, + }, +}; +// const clean = async () => {}; +// const file_id = 'file-X4ZR3WmEyxJKOEuyuthIIj9T'; +// const ftJobId = 'ftjob-WUootDAvgqK1iJlpTn1XZmoO'; +const modelId = 'gpt-3.5-turbo-16k'; + +test('Chat message', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await chat.message({ text: 'Hello' }); + + assert.strictEqual(typeof res, 'string'); +}); + +test('Chat voice message', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await chat.voiceMessage({ + inputFilePath: AUDIOS + '/test-speech-input-en.mp3', + outputFilePath: AUDIOS + '/my_test-speech-output-en.mp3', + returnIntermediateResult: false, + }); + + assert.ok('inputText' in res); + assert.strictEqual(res.inputText, 'Hello there.'); + assert.ok('outputText' in res); + assert.ok('outputFilePath' in res); +}); + +test('Text completion', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await language(chat.openai).generate({ text: 'hello' }); + + assert.ok('message' in res); + assert.ok('messages' in res); + assert.ok('usage' in res); +}); + +test('Text completeon with function call', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await language(chat.openai).generate({ + text: 'What is the weather like in San Francisco, Tokyo and Paris?', + tools: TEST_LIBRARY.tools, + }); + + assert.ok('message' in res); + assert.ok('messages' in res); + assert.ok('usage' in res); +}); + +test('Text Embedding', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await language(chat.openai).embedding({ text: 'hello' }); + + assert.ok('embedding' in res); + assert.ok('usage' in res); +}); + +// WARN: 400 Invalid value for 'model' = text-moderation-007. + +// await t.test('Text Classification', async () => { +// const chat = new Chat({ apiKey: API_KEY }); +// +// const res = await language(chat.openai).classification({ +// text: 'I will kill you boatman', +// }); +// +// assert.strictEqual(res.flagged, true); +// assert.strictEqual(res.categories.violence, true); +// assert.ok('violence' in res.category_scores); +// }); + +// TODO: We need update test to without statement (tested.fineTune.file) +test('Count file tokens', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await files(chat.openai).countFileTokens({ + pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', + purpose: 'fine-tune', + }); + + assert.strictEqual(res, 1889); +}); + +test('Creare fine-tune file', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await files(chat.openai).create({ + pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', + purpose: 'fine-tune', + }); + + assert.ok('id' in res); + assert.strictEqual(res.status, 'processed'); + + tested.fineTune.file.id = res.id; +}); + +test('Create assistant file', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await files(chat.openai).create({ + pathToFile: ASSISTANTS + '/test.csv', + purpose: 'assistants', + }); + + assert.ok('id' in res); + assert.strictEqual(res.status, 'processed'); + + tested.assistant.file.id = res.id; +}); + +test('List files', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await files(chat.openai).list(); + + assert.ok(Array.isArray(res)); +}); + +test('Retrieve file', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const file_id = tested.fineTune.file.id; + const res = await files(chat.openai).retrieve({ file_id }); + + assert.ok('id' in res); + assert.strictEqual(res.id, file_id); +}); + +test('Retrieve file content', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const file_id = tested.fineTune.file.id; + const res = await files(chat.openai).content({ file_id }); + + assert.strictEqual(typeof res, 'string'); +}); + +test('Delete file', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const file_id = tested.fineTune.file.id; + const res = await files(chat.openai).del({ file_id }); + + assert.ok('id' in res); + assert.strictEqual(res.id, file_id); + assert.strictEqual(res.deleted, true); +}); + +test('Create Fine Tune job', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await fineTune(chat.openai).create({ + pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', + }); + + assert.ok('id' in res); + assert.deepEqual(res.error, {}); + + tested.fineTune.id = res.id; + tested.fineTune.file.id = res.training_file; +}); + +test('Create Fine Tune from training file', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const file_id = tested.fineTune.file.id; + const res = await fineTune(chat.openai).create({ + training_file: file_id, + }); + + assert.ok('id' in res); + assert.deepEqual(res.error, {}); + + tested.fineTune.id = res.id; +}); +// // Does not work +// // test('Get Fine Tune events', async () => { +// // // let ftJobId = tested.fineTune.id; +// // const res = await fineTune.events({id:ftJobId}) +// // }); +test('List Fine Tune jobs', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await fineTune(chat.openai).list(); + + assert.ok(Array.isArray(res)); +}); + +test('Retrieve Fine Tune job', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const ftJobId = tested.fineTune.id; + const res = await fineTune(chat.openai).retrieve({ id: ftJobId }); + + assert.ok('id' in res); + assert.strictEqual(res.id, ftJobId); +}); +// Require to catch error if job is completed +// test('Cancel Fine Tune job', async () => { +// // let ftJobId = tested.fineTune.id; +// const fn = async () => await fineTune.cancel({id:ftJobId}); +// expect(fn).toThrow(TypeError); +// }); + +test('List models', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await models(chat.openai).list(); + + assert.ok(Array.isArray(res)); + + const custom_model = res.find((model) => model.id.startsWith('ft')); + tested.model.id = custom_model.id; +}); + +test('Retrieve model', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const res = await models(chat.openai).retrieve({ model_id: modelId }); + + assert.ok('id' in res); + assert.strictEqual(res.id, modelId); +}); + +// WARN: 403 You have insufficient permissions for this operation + +// await t.test('Delete model', async () => { +// const chat = new Chat({ apiKey: API_KEY }); +// +// const res = await models(chat.openai).del({ model_id: modelId }); +// +// assert.ok('deleted' in res); +// assert.ok(res.deleted); +// }); + +test('textToSpeech', async (t) => { + const chat = new Chat({ apiKey: API_KEY }); + + const pathToFile = AUDIOS + '/my_test-speech-output-en.mp3'; + + const isExist = await common.fileIsExist(pathToFile); + + if (isExist) { + try { + await fs.promises.unlink(pathToFile); + } catch (err) { + t.fail(err); + } + } + + const res = await speech(chat.openai).textToSpeech({ + text: 'Hello, how can I help you?', + pathToFile, + }); + const stat = await fs.promises.stat(pathToFile); + + assert.ok(Buffer.isBuffer(res)); + assert.ok('uid' in stat); +}); + +test('speechToText', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const pathToFile = AUDIOS + '/test-speech-input-en.mp3'; + const res = await speech(chat.openai).speechToText({ pathToFile }); + + assert.strictEqual(typeof res, 'string'); +}); + +test('Speech Translation', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const pathToFile = AUDIOS + '/test-speech-input-ru.mp3'; + const res = await speech(chat.openai).speechTranslation({ pathToFile }); + + assert.strictEqual(typeof res, 'string'); +}); + +test('Image Generation', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const saveAs = IMAGES + '/my_test-image-create-result.jpg'; + + const res = await images(chat.openai).create({ + text: 'a white siamese cat', + saveAs, + }); + + assert.ok('url' in res); + assert.ok('local' in res); +}); + +test('Image Edit', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const saveAs = IMAGES + '/my_test-edit-image-result.jpg'; + + const res = await images(chat.openai).edit({ + text: 'A futuristic landscape behind a foregraund emoticon', + pathToFile: IMAGES + '/test-edit-image.png', + pathToMask: IMAGES + '/test-edit-image.png', + saveAs, + size: '256x256', + }); + + assert.ok('url' in res); + assert.ok('local' in res); +}); + +test('Image Variation', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const pathToFile = IMAGES + '/test-edit-image.png'; + const saveAs = IMAGES + '/my_test-image-variation-result.jpg'; + // try { + // await fs.promises.unlink(saveAs) + // } catch (error) {} + + const res = await images(chat.openai).variation({ pathToFile, saveAs }); + // const stat = await fs.promises.stat(saveAs); + + assert.ok('url' in res); + assert.ok('local' in res); + // expect(stat).toHaveProperty('uid'); +}); + +test('Image Recognition', async () => { + const chat = new Chat({ apiKey: API_KEY }); + + const pathToFile = IMAGES + '/test-image.jpg'; + const res = await recognition(chat.openai).image({ pathToFile }); + + assert.ok('message' in res); + assert.ok('usage' in res); +}); +// .....Too heavy for testing +// test('Video Recognition', async () => { +// const pathToFile = './lib/LLM/OpenAI/tests/videos/cat-no.mp4'; +// const outputDir = './lib/LLM/OpenAI/tests/videos/video-frames' +// const res = await recognition.video({ +// pathToFile, +// outputDir, +// frameRate: 5, +// }); +// expect(res).toHaveProperty('message'); +// expect(res).toHaveProperty('usage'); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8a74313 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ESNext", + "moduleResolution": "node", + "strict": true, + "noEmit": true, + "baseUrl": ".", + "preserveWatchOutput": true + }, + "include": ["*.d.ts"] +} diff --git a/utilities.js b/utilities.js deleted file mode 100644 index 83acdfa..0000000 --- a/utilities.js +++ /dev/null @@ -1,204 +0,0 @@ -require('dotenv').config(); -const { LOG_API_CALL_TIME, LOG_API_CALL_RESULT, LOG_API_CALL_ERRORS } = require('./config'); -const fs = require ("fs"); -const path = require('path'); -const events = require('events'); -const readline = require('readline'); -const { Readable } = require ("stream"); -const math = require('@dip1059/safe-math-js'); -// const { TEST, LOG_TIME } = process.env; - -// const _ = require('lodash'); - -const callAPI = async (library, methodPath, ...args) => { - - - const methodName = methodPath.split('.').pop(); - const method = library[methodName]; - - if (typeof method !== 'function') { - const message = `Method ${methodPath} not found`; - throw new Error(message) - } - - let res, start, spentTime; - - - - // if ( LOG_API_CALL_TIME) - start = measureTime(); - - try { - res = await method.call(library, ...args); - } catch(error) { - const message = `Error occured while calling ${methodPath}`; - if (LOG_API_CALL_ERRORS) console.error(message, error); - return {error}; - } - - spentTime = measureTime(start) - if (LOG_API_CALL_TIME) console.log({[methodPath]:spentTime}); - if (LOG_API_CALL_RESULT) console.log(`${methodPath} result:`, res); - - return res; - } - - - const measureTime = (start, format = true) => { - if (! LOG_API_CALL_TIME) return; - const NS_PER_SEC = 1e9; - const NUM_IN_MS = 1000000; - if (start) { - const diff = process.hrtime(start); - const time = format ? - ((diff[0] ? diff[0] + ' sec, ' : '') + (diff[1]/NUM_IN_MS).toFixed(3) + 'ms') : - parseFloat((diff[0] * 1000 + (diff[1] / NUM_IN_MS).toFixed(3))); - return time; - } - return process.hrtime(); - } - - - - // const formatMilliseconds = (milliseconds) => { - // const ms = milliseconds % 1000; - // milliseconds = (milliseconds - ms) / 1000; - // } - - - const delay = (time) => { - return new Promise(function(resolve) { - setTimeout(resolve, time) - }); - } - - const random = (min, max) => { - return Math.round(Math.random() * (max - min) + min); - } - - const roughSizeOfObject = (value) => { - const typeSizes = { - "undefined": () => 0, - "boolean": () => 4, - "number": () => 8, - "bigint": () => 8, - "string": item => 2 * item.length, - "object": item => !item ? 0 : Object - .keys(item) - .reduce((total, key) => ut.roughSizeOfObject(key) + ut.roughSizeOfObject(item[key]) + total, 0) - }; - return typeSizes[typeof value](value) - } - - const formatBytes = (bytes, decimals = 2) => { - if (bytes === 0) return '0 Bytes'; - - const k = 1024; - const dm = decimals < 0 ? 0 : decimals; - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - - const i = Math.floor(Math.log(bytes) / Math.log(k)); - - return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; - } - - const humanFileSize = (bytes, si=false, dp=1) => { - const thresh = si ? 1000 : 1024; - - if (Math.abs(bytes) < thresh) { - return bytes + ' B'; - } - - const units = si - ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] - : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; - let u = -1; - const r = 10**dp; - - do { - bytes /= thresh; - ++u; - } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1); - - - return bytes.toFixed(dp) + ' ' + units[u]; - } - - const extractVideoFrames = async (videoPath, outputDir, frameRate = 1, prefix = 'frame') => { - const ffmpeg = require('fluent-ffmpeg'); - return new Promise((resolve, reject) => { - ffmpeg(videoPath) - .outputOptions([ - '-vf fps=' + frameRate, - '-q:v 15' - // '-update 1' - ]) - .output(outputDir + `/${prefix}%04d.jpg`) - .on('end', () => { - resolve(); - }) - .on('error', (err) => { - reject(err); - }) - .run(); - }); - } - - const cleanDirectory = async (dir) => { - try { - const files = await fs.promises.readdir(dir); - for (const file of files) { - await fs.promises.unlink(path.join(dir, file)); - } - } catch (error) { - console.error('Error cleaning directory:', error); - } - } - - const processFileLineByLine = async (filePath, processLineCallback, finishCallback) => { - let index = 0, stat; - const startMemory = process.memoryUsage().heapUsed, startTime = measureTime(); - try { - stat = fs.statSync(filePath); - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); - - rl.on('line', (line) => { - // console.log(`Line from file: ${line}`); - index++; - processLineCallback({line, index}); - }); - - await events.once(rl, 'close'); - - if (typeof finishCallback === 'function') { - const size = humanFileSize(stat.size, true); - const memoryUsed = (process.memoryUsage().heapUsed - startMemory) / 1024 / 1024; - const MB = Math.round(memoryUsed * 100) / 100 + ' MB'; - const time = measureTime(startTime); - - finishCallback({filePath, size, lines:index, used: MB, time}); - } - } catch (err) { - console.error(err); - } - } - - const saveFileFromWeb = async (url, path) => { - const res = await fetch(url); - if (!res.ok || !res.body) return({error:`unexpected response ${res.statusText}`}); - let writer = fs.createWriteStream(path); - Readable.fromWeb(res.body).pipe(writer); - } - - - - - - - - -module.exports = {callAPI, measureTime, math, delay, random, roughSizeOfObject, formatBytes, - humanFileSize, extractVideoFrames, cleanDirectory, processFileLineByLine, saveFileFromWeb }; \ No newline at end of file From bddc8d7f0fe58904e0064debb49fc340732b8e4a Mon Sep 17 00:00:00 2001 From: Dmitriy Tselinko Date: Wed, 15 May 2024 10:54:47 +0200 Subject: [PATCH 2/5] Huggingface refactor PR-URL: https://github.com/Universal-Code-Modules/universal-llm/pull/3 --- lib/huggingface/config.json | 42 ++ lib/huggingface/connector.js | 543 ++++-------------------- lib/huggingface/utils/audio.js | 50 +++ lib/huggingface/utils/computerVision.js | 94 ++++ lib/huggingface/utils/custom.js | 59 +++ lib/huggingface/utils/index.js | 10 + lib/huggingface/utils/language.js | 158 +++++++ lib/huggingface/utils/multimodal.js | 53 +++ lib/huggingface/utils/tabular.js | 59 +++ test/huggingface.js | 439 ------------------- test/huggingface/audio.js | 66 +++ test/huggingface/computerVision.js | 129 ++++++ test/huggingface/custom.js | 49 +++ test/huggingface/language.js | 181 ++++++++ test/huggingface/multimodal.js | 68 +++ test/huggingface/tabular.js | 62 +++ 16 files changed, 1165 insertions(+), 897 deletions(-) create mode 100644 lib/huggingface/config.json create mode 100644 lib/huggingface/utils/audio.js create mode 100644 lib/huggingface/utils/computerVision.js create mode 100644 lib/huggingface/utils/custom.js create mode 100644 lib/huggingface/utils/index.js create mode 100644 lib/huggingface/utils/language.js create mode 100644 lib/huggingface/utils/multimodal.js create mode 100644 lib/huggingface/utils/tabular.js delete mode 100644 test/huggingface.js create mode 100644 test/huggingface/audio.js create mode 100644 test/huggingface/computerVision.js create mode 100644 test/huggingface/custom.js create mode 100644 test/huggingface/language.js create mode 100644 test/huggingface/multimodal.js create mode 100644 test/huggingface/tabular.js diff --git a/lib/huggingface/config.json b/lib/huggingface/config.json new file mode 100644 index 0000000..7566a45 --- /dev/null +++ b/lib/huggingface/config.json @@ -0,0 +1,42 @@ +{ + "DEFAULT_MODELS": { + "language": { + "fillMask": "bert-base-uncased", + "summarization": "facebook/bart-large-cnn", + "questionAnswering": "deepset/roberta-base-squad2", + "tableQuestionAnswering": "google/tapas-base-finetuned-wtq", + "textClassification": "distilbert-base-uncased-finetuned-sst-2-english", + "textGeneration": "gpt2", + "textGenerationStream": "google/flan-t5-xxl", + "tokenClassification": "dbmdz/bert-large-cased-finetuned-conll03-english", + "translation": "t5-base", + "zeroShotClassification": "facebook/bart-large-mnli", + "sentenceSimilarity": "sentence-transformers/paraphrase-xlm-r-multilingual-v1" + }, + "audio": { + "automaticSpeechRecognition": "facebook/wav2vec2-large-960h-lv60-self", + "audioClassification": "superb/hubert-large-superb-er", + "textToSpeech": "espnet/kan-bayashi_ljspeech_vits", + "audioToAudio": "speechbrain/sepformer-wham" + }, + "computerVision": { + "imageClassification": "google/vit-base-patch16-224", + "objectDetection": "facebook/detr-resnet-50", + "imageSegmentation": "facebook/detr-resnet-50-panoptic", + "imageToText": "nlpconnect/vit-gpt2-image-captioning", + "textToImage": "stabilityai/stable-diffusion-2", + "imageToImage": "lllyasviel/sd-controlnet-depth", + "zeroShotImageClassification": "openai/clip-vit-large-patch14-336" + }, + "multimodal": { + "featureExtraction": "sentence-transformers/distilbert-base-nli-mean-tokens", + "visualQuestionAnswering": "dandelin/vilt-b32-finetuned-vqa", + "documentQuestionAnswering": "impira/layoutlm-document-qa" + }, + "tabular": { + "tabularRegression": "scikit-learn/Fish-Weight", + "tabularClassification": "vvmnnnkv/wine-quality" + } + }, + "DEFAULT_VOICE": "onyx" +} diff --git a/lib/huggingface/connector.js b/lib/huggingface/connector.js index 451ec31..67ec55b 100644 --- a/lib/huggingface/connector.js +++ b/lib/huggingface/connector.js @@ -1,478 +1,105 @@ 'use strict'; const { HfInference } = require('@huggingface/inference'); -const { callAPI } = require('../common.js'); -const HUGGINGFACE_TOKEN = process.env.HUGGINGFACE_TOKEN; - -const hf = new HfInference(HUGGINGFACE_TOKEN); - -// You can also omit "model" to use the recommended model for the task - -// constructor() { - -// } - -//......Natural Language Processing -// inputs = '[MASK] world!' - -const FillMask = async (inputs, model = 'bert-base-uncased') => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.fillMask', args); - return res; -}; - -/* - inputs = `The tower is 324 metres (1,063 ft) tall, about the same height - as an 81-storey building, and the tallest structure in Paris. - Its base is square, measuring 125 metres (410 ft) on each side. - During its construction, the Eiffel Tower surpassed the Washington - Monument to become the tallest`, - model = 'facebook/bart-large-cnn' -*/ - -const Summarization = async ( - inputs, - parameters = { max_length: 100 }, - model = 'facebook/bart-large-cnn', -) => { - const args = { inputs, parameters, model }; - const res = await callAPI(hf, 'hf.summarization', args); - return res; -}; +const utils = require('./utils'); +const { DEFAULT_MODELS } = require('./config.json'); + +const { tokens, custom } = utils; + +class Chat { + //temperature = 0.7, topP = 1, frequencyPenalty = 0 + //presencePenalty = 0, stop = ["\n", ""] + constructor({ + apiKey, + system, + model = DEFAULT_MODELS.completions, + tools, + maxTokens = 1000, + // maxPrice = 0.1, + }) { + this.hf = new HfInference(apiKey); + this.system = system; + this.model = model; + this.tools = tools; + this.maxTokens = maxTokens; + // this.maxPrice = maxPrice; + + this.messages = []; + this.tokens = 0; + this.price = 0; + + // throw new Error(`Max ${maxTokens} tokens exceeded`); + } -/* - inputs = { - question: 'What is the capital of France?', - context: 'The capital of France is Paris.' - }, -*/ + async message({ text }) { + const tokenCount = await tokens.count({ text, model: this.model }); + const { maxTokens, model } = this; -const QuestionAnswering = async ( - inputs, - model = 'deepset/roberta-base-squad2', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.questionAnswering', args); - return res; -}; - -/* - inputs = { - query: 'How many stars does the transformers repository have?', - table: { - Repository: ['Transformers', 'Datasets', 'Tokenizers'], - Stars: ['36542', '4512', '3934'], - Contributors: ['651', '77', '34'], - 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'] + const increaseMaxTokens = tokens + tokenCount > maxTokens; + if (increaseMaxTokens) { + throw new Error(`Max ${this.maxTokens} tokens exceeded`); } - }, -*/ - -const TableQuestionAnswering = async ( - inputs, - model = 'google/tapas-base-finetuned-wtq', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.tableQuestionAnswering', args); - return res; -}; - -// inputs = 'I like you. I love you.' - -const TextClassification = async ( - inputs, - model = 'distilbert-base-uncased-finetuned-sst-2-english', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.textClassification', args); - return res; -}; - -// inputs = 'The answer to the universe is' - -const TextGeneration = async (inputs, model = 'gpt2') => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.textGeneration', args); - return res; -}; -// inputs = 'repeat "one two three four"' -// parameters = { max_new_tokens: 250 } - -const TextGenerationStream = async ( - inputs, - parameters = {}, - model = 'google/flan-t5-xxl', -) => { - const args = { inputs, parameters, model }; - const res = await callAPI(hf, 'hf.textGenerationStream', args); - return res; -}; -// inputs = 'My name is Sarah Jessica Parker but you can call me Jessica' - -const TokenClassification = async ( - inputs, - model = 'dbmdz/bert-large-cased-finetuned-conll03-english', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.tokenClassification', args); - return res; -}; + const res = await custom(this.hf).generate({ + text, + model, + messages: this.messages, + system: this.system, + tools: this.tools, + }); -// inputs = 'My name is Wolfgang and I live in Amsterdam', -// parameters = {"src_lang": "en_XX", "tgt_lang": "fr_XX"} + if (res.error) return res.error.message; -const Translation = async (inputs, parameters = {}, model = 't5-base') => { - const args = { inputs, parameters, model }; - const res = await callAPI(hf, 'hf.translation', args); - return res; -}; -/* - inputs = [ - 'Hi, I recently bought a device from your company but it is not working' + - ' as advertised and I would like to get reimbursed!' - ], - parameters = { candidate_labels: ['refund', 'legal', 'faq'] } -*/ -const ZeroShotClassification = async ( - inputs, - parameters = {}, - model = 'facebook/bart-large-mnli', -) => { - const args = { inputs, parameters, model }; - const res = await callAPI(hf, 'hf.zeroShotClassification', args); - return res; -}; + this.messages = res.messages; + this.tokens += res.usage.total_tokens; + this.price += res.usage.total_price; -/* - inputs = { - source_sentence: 'That is a happy person', - sentences: [ - 'That is a happy dog', - 'That is a very happy person', - 'Today is a sunny day' - ] + return res.message; } -*/ - -const SentenceSimilarity = async ( - inputs, - model = 'sentence-transformers/paraphrase-xlm-r-multilingual-v1', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.sentenceSimilarity', args); - return res; -}; - -//.........Audio -// data = readFileSync('test/sample1.flac') - -const AutomaticSpeechRecognition = async ( - data, - model = 'facebook/wav2vec2-large-960h-lv60-self', -) => { - const args = { data, model }; - const res = await callAPI(hf, 'hf.automaticSpeechRecognition', args); - return res; -}; - -// data = readFileSync('test/sample1.flac') - -const AudioClassification = async ( - data, - model = 'superb/hubert-large-superb-er', -) => { - const args = { data, model }; - const res = await callAPI(hf, 'hf.audioClassification', args); - return res; -}; - -// inputs = 'Hello world!' - -const TextToSpeech = async ( - inputs, - model = 'espnet/kan-bayashi_ljspeech_vits', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.textToSpeech', args); - return res; -}; -/* - data = readFileSync('test/sample1.flac') - */ -const AudioToAudio = async (data, model = 'speechbrain/sepformer-wham') => { - const args = { data, model }; - const res = await callAPI(hf, 'hf.audioToAudio', args); - return res; -}; - -//........Computer Vision -// data = readFileSync('test/cheetah.png') - -const ImageClassification = async ( - data, - model = 'google/vit-base-patch16-224', -) => { - const args = { data, model }; - const res = await callAPI(hf, 'hf.imageClassification', args); - return res; -}; -/* - data = readFileSync('test/cats.png') - */ -const ObjectDetection = async (data, model = 'facebook/detr-resnet-50') => { - const args = { data, model }; - const res = await callAPI(hf, 'hf.objectDetection', args); - return res; -}; - -// data = readFileSync('test/cats.png') - -const ImageSegmentation = async ( - data, - model = 'facebook/detr-resnet-50-panoptic', -) => { - const args = { data, model }; - const res = await callAPI(hf, 'hf.imageSegmentation', args); - return res; -}; - -// data = await (await fetch('https://picsum.photos/300/300')).blob() - -const ImageToText = async ( - data, - model = 'nlpconnect/vit-gpt2-image-captioning', -) => { - const args = { data, model }; - const res = await callAPI(hf, 'hf.imageToText', args); - return res; -}; - -/* - inputs = 'award winning high resolution photo of a giant' + - ' tortoise/((ladybird)) hybrid, [trending on artstation]', - parameters = {negative_prompt: 'blurry'}, -*/ - -const TextToImage = async ( - inputs, - parameters = {}, - model = 'stabilityai/stable-diffusion-2', -) => { - const args = { inputs, parameters, model }; - const res = await callAPI(hf, 'hf.textToImage', args); - return res; -}; - -/* - inputs = new Blob([readFileSync("test/stormtrooper_depth.png")]), - parameters = {prompt: "elmo's lecture"}, -*/ - -const ImageToImage = async ( - inputs, - parameters = {}, - model = 'lllyasviel/sd-controlnet-depth', -) => { - const args = { inputs, parameters, model }; - const res = await callAPI(hf, 'hf.imageToImage', args); - return res; -}; - -/* - inputs = { image: await (await fetch('https://placekitten.com/300/300')).blob() }, - parameters = { candidate_labels: ['cat', 'dog'] }, -*/ - -const ZeroShotImageClassification = async ( - inputs, - parameters = {}, - model = 'openai/clip-vit-large-patch14-336', -) => { - const args = { inputs, parameters, model }; - const res = await callAPI(hf, 'hf.zeroShotImageClassification', args); - return res; -}; - -//......Multimodal -// inputs = "That is a happy person", - -const FeatureExtraction = async ( - inputs, - model = 'sentence-transformers/distilbert-base-nli-mean-tokens', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.featureExtraction', args); - return res; -}; - -/* - inputs = { - question: 'How many cats are lying down?', - image: await (await fetch('https://placekitten.com/300/300')).blob() - }, -*/ - -const VisualQuestionAnswering = async ( - inputs, - model = 'dandelin/vilt-b32-finetuned-vqa', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.visualQuestionAnswering', args); - return res; -}; - -/* - inputs = { - question: 'Invoice number?', - image: await (await fetch('https://huggingface.co/spaces/impira/docquery/resolve/2359223c1837a7587402bda0f2643382a6eefeab/invoice.png')).blob(), - }, -*/ -const DocumentQuestionAnswering = async ( - inputs, - model = 'impira/layoutlm-document-qa', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.documentQuestionAnswering', args); - return res; -}; - -//.....Tabular -/* - inputs = { - data: { - "Height": ["11.52", "12.48", "12.3778"], - "Length1": ["23.2", "24", "23.9"], - "Length2": ["25.4", "26.3", "26.5"], - "Length3": ["30", "31.2", "31.1"], - "Species": ["Bream", "Bream", "Bream"], - "Width": ["4.02", "4.3056", "4.6961"] - }, - }, -*/ - -const TabularRegression = async ( - inputs, - model = 'scikit-learn/Fish-Weight', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.tabularRegression', args); - return res; -}; - -/* - inputs = { - data: { - "fixed_acidity": ["7.4", "7.8", "10.3"], - "volatile_acidity": ["0.7", "0.88", "0.32"], - "citric_acid": ["0", "0", "0.45"], - "residual_sugar": ["1.9", "2.6", "6.4"], - "chlorides": ["0.076", "0.098", "0.073"], - "free_sulfur_dioxide": ["11", "25", "5"], - "total_sulfur_dioxide": ["34", "67", "13"], - "density": ["0.9978", "0.9968", "0.9976"], - "pH": ["3.51", "3.2", "3.23"], - "sulphates": ["0.56", "0.68", "0.82"], - "alcohol": ["9.4", "9.8", "12.6"] - }, - }, -*/ - -const TabularClassification = async ( - inputs, - model = 'vvmnnnkv/wine-quality', -) => { - const args = { inputs, model }; - const res = await callAPI(hf, 'hf.tabularClassification', args); - return res; -}; - -//........Custom -/* - inputs = "hello world", - parameters = { - custom_param: 'some magic', + /* + "text" argument - in case we do conversion on front end + */ + async voiceMessage() { + throw new Error('Not Implemented'); } -*/ -const CustomCall = async ( - inputs, - parameters = {}, - model = 'my-custom-model', -) => { - const args = { inputs, parameters, model }; - const res = await callAPI(hf, 'hf.request', args); - return res; -}; - -/* - inputs = "hello world", - parameters = { - custom_param: 'some magic', + async voiceAnswer() { + throw new Error('Not Implemented'); } -*/ - -const CustomCallStreaming = async ( - inputs, - parameters = {}, - model = 'my-custom-model', -) => { - const args = { inputs, parameters, model }; - return this._makeApiCall('streamingRequest', args); -}; - -/* - inputs = 'The answer to the universe is', - endpoint = 'https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2' -*/ - -const CustomInferenceEndpoint = async (inputs, endpoint) => { - const args = { inputs }; - const hfEndpoint = hf.endpoint(endpoint); - try { - const res = await hfEndpoint.textGeneration(args); - return res; - } catch (err) { - throw new Error('Error occured while triggering "textGeneration" method', { - cause: err, - }); +} + +class Assistant { + constructor({ + assistant_id, + thread_id, + /* + model = DEFAULT_MODELS.completions, + maxTokens = 1000, + maxPrice = 0.1, + */ + }) { + this.id = assistant_id; + this.thread_id = thread_id; + // this.model = model; + // this.maxTokens = maxTokens; + // this.maxPrice = maxPrice; + + this.messages = []; + // this.tokens = 0; + // this.price = 0; } -}; -module.exports = { - FillMask, - Summarization, - QuestionAnswering, - TableQuestionAnswering, - TextClassification, - TextGeneration, - TextGenerationStream, - TokenClassification, - Translation, - ZeroShotClassification, - SentenceSimilarity, - - AutomaticSpeechRecognition, - AudioClassification, - TextToSpeech, - AudioToAudio, - - ImageClassification, - ObjectDetection, - ImageSegmentation, - ImageToText, - TextToImage, - ImageToImage, - ZeroShotImageClassification, - FeatureExtraction, + // message({ text }) {} +} - VisualQuestionAnswering, - DocumentQuestionAnswering, - TabularRegression, - TabularClassification, - CustomCall, - CustomCallStreaming, - CustomInferenceEndpoint, +module.exports = { + Chat, + Assistant, }; + +// Class chat +// chat.message({text}) => {message, messages, usages} => string +// chat.voiceMessage({inputFilePath, outputFilePath, voice}) => +// {inputText, outputText, outputFilePath} diff --git a/lib/huggingface/utils/audio.js b/lib/huggingface/utils/audio.js new file mode 100644 index 0000000..34d0597 --- /dev/null +++ b/lib/huggingface/utils/audio.js @@ -0,0 +1,50 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const defaultModels = DEFAULT_MODELS.audio; + +//......Audio....... + +const audio = (hf) => ({ + + // data = readFileSync('test/sample1.flac') + async automaticSpeechRecognition( + data, + model = defaultModels.automaticSpeechRecognition, + ) { + const args = { data, model }; + const res = await callAPI(hf, 'hf.automaticSpeechRecognition', args); + return res; + }, + + // data = readFileSync('test/sample1.flac') + async audioClassification( + data, + model = defaultModels.audioClassification, + ) { + const args = { data, model }; + const res = await callAPI(hf, 'hf.audioClassification', args); + return res; + }, + + // inputs = 'Hello world!' + async textToSpeech( + inputs, + model = defaultModels.textToSpeech, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.textToSpeech', args); + return res; + }, + + // data = readFileSync('test/sample1.flac') + async audioToAudio(data, model = defaultModels.audioToAudio) { + const args = { data, model }; + const res = await callAPI(hf, 'hf.audioToAudio', args); + return res; + } +}); + +module.exports = { audio }; diff --git a/lib/huggingface/utils/computerVision.js b/lib/huggingface/utils/computerVision.js new file mode 100644 index 0000000..63655ad --- /dev/null +++ b/lib/huggingface/utils/computerVision.js @@ -0,0 +1,94 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const defaultModels = DEFAULT_MODELS.computerVision; + +//......ComputerVision....... + +const computerVision = (hf) => ({ + // data = readFileSync('test/cheetah.png') + async imageClassification( + data, + model = defaultModels.imageClassification, + ) { + const args = { data, model }; + const res = await callAPI(hf, 'hf.imageClassification', args); + return res; + }, + + /* + data = readFileSync('test/cats.png') + */ + async objectDetection(data, model = defaultModels.objectDetection) { + const args = { data, model }; + const res = await callAPI(hf, 'hf.objectDetection', args); + return res; + }, + + // data = readFileSync('test/cats.png') + async imageSegmentation( + data, + model = defaultModels.imageSegmentation, + ) { + const args = { data, model }; + const res = await callAPI(hf, 'hf.imageSegmentation', args); + return res; + }, + + // data = await (await fetch('https://picsum.photos/300/300')).blob() + async imageToText( + data, + model = defaultModels.imageToText, + ) { + const args = { data, model }; + const res = await callAPI(hf, 'hf.imageToText', args); + return res; + }, + + /* + inputs = 'award winning high resolution photo of a giant' + + ' tortoise/((ladybird)) hybrid, [trending on artstation]', + parameters = {negative_prompt: 'blurry'}, + */ + async textToImage( + inputs, + parameters = {}, + model = defaultModels.textToImage, + ) { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.textToImage', args); + return res; + }, + + /* + inputs = new Blob([readFileSync("test/stormtrooper_depth.png")]), + parameters = {prompt: "elmo's lecture"}, + */ + async imageToImage( + inputs, + parameters = {}, + model = defaultModels.imageToImage, + ) { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.imageToImage', args); + return res; + }, + + /* + inputs = { image: await (await fetch('https://placekitten.com/300/300')).blob() }, + parameters = { candidate_labels: ['cat', 'dog'] }, + */ + async zeroShotImageClassification( + inputs, + parameters = {}, + model = defaultModels.zeroShotImageClassification, + ) { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.zeroShotImageClassification', args); + return res; + }, +}); + +module.exports = { computerVision }; diff --git a/lib/huggingface/utils/custom.js b/lib/huggingface/utils/custom.js new file mode 100644 index 0000000..95dc5d5 --- /dev/null +++ b/lib/huggingface/utils/custom.js @@ -0,0 +1,59 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); + +//......Custom....... + +const custom = (hf) => ({ + /* + inputs = "hello world", + parameters = { + custom_param: 'some magic', + } + */ + async customCall( + inputs, + parameters = {}, + model, + ) { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.request', args); + return res; + }, + + /* + inputs = "hello world", + parameters = { + custom_param: 'some magic', + } + */ + async customCallStreaming( + inputs, + parameters = {}, + model, + ) { + const args = { inputs, parameters, model }; + return callAPI(hf, 'hf.streamingRequest', args); + }, + + /* + inputs = 'The answer to the universe is', + endpoint = 'https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2' + */ + async customInferenceEndpoint(inputs, endpoint) { + const args = { inputs }; + const hfEndpoint = hf.endpoint(endpoint); + try { + const res = await hfEndpoint.textGeneration(args); + return res; + } catch (err) { + throw new Error( + 'Error occured while triggering "textGeneration" method', + { + cause: err, + }); + } + } +}); + +module.exports = { custom }; diff --git a/lib/huggingface/utils/index.js b/lib/huggingface/utils/index.js new file mode 100644 index 0000000..83452a7 --- /dev/null +++ b/lib/huggingface/utils/index.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + ...require('./audio'), + ...require('./custom'), + ...require('./computerVision'), + ...require('./language'), + ...require('./multimodal'), + ...require('./tabular'), +}; diff --git a/lib/huggingface/utils/language.js b/lib/huggingface/utils/language.js new file mode 100644 index 0000000..d6340dc --- /dev/null +++ b/lib/huggingface/utils/language.js @@ -0,0 +1,158 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const defaultModels = DEFAULT_MODELS.language; + +//......Natural Language Processing (language)....... + +const language = (hf) => ({ + // inputs = '[MASK] world!' + async fillMask(inputs, model = defaultModels.fillMask) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.fillMask', args); + return res; + }, + + /* + inputs = `The tower is 324 metres (1,063 ft) tall, about the same height + as an 81-storey building, and the tallest structure in Paris. + Its base is square, measuring 125 metres (410 ft) on each side. + During its construction, the Eiffel Tower surpassed the Washington + Monument to become the tallest`, + model = 'facebook/bart-large-cnn' + */ + async summarization( + inputs, + parameters = { max_length: 100 }, + model = defaultModels.summarization, + ) { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.summarization', args); + return res; + }, + + /* + inputs = { + question: 'What is the capital of France?', + context: 'The capital of France is Paris.' + }, + */ + async questionAnswering( + inputs, + model = defaultModels.questionAnswering, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.questionAnswering', args); + return res; + }, + + /* + inputs = { + query: 'How many stars does the transformers repository have?', + table: { + Repository: ['Transformers', 'Datasets', 'Tokenizers'], + Stars: ['36542', '4512', '3934'], + Contributors: ['651', '77', '34'], + 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'] + } + }, + */ + async tableQuestionAnswering( + inputs, + model = defaultModels.tableQuestionAnswering, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.tableQuestionAnswering', args); + return res; + }, + + // inputs = 'I like you. I love you.' + async textClassification( + inputs, + model = defaultModels.textClassification, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.textClassification', args); + return res; + }, + + // inputs = 'The answer to the universe is' + async textGeneration(inputs, model = defaultModels.textGeneration) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.textGeneration', args); + return res; + }, + + // inputs = 'repeat "one two three four"' + // parameters = { max_new_tokens: 250 } + async textGenerationStream( + inputs, + parameters = {}, + model = defaultModels.textGenerationStream, + ) { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.textGenerationStream', args); + return res; + }, + + // inputs = 'My name is Sarah Jessica Parker but you can call me Jessica' + async tokenClassification( + inputs, + model = defaultModels.tokenClassification, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.tokenClassification', args); + return res; + }, + + // inputs = 'My name is Wolfgang and I live in Amsterdam', + // parameters = {"src_lang": "en_XX", "tgt_lang": "fr_XX"} + async translation( + inputs, parameters = {}, model = defaultModels.translation + ) { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.translation', args); + return res; + }, + + /* + inputs = [ + 'Hi, I recently bought a device from your company but it is not working' + + ' as advertised and I would like to get reimbursed!' + ], + parameters = { candidate_labels: ['refund', 'legal', 'faq'] } + */ + async zeroShotClassification( + inputs, + parameters = {}, + model = defaultModels.zeroShotClassification, + ) { + const args = { inputs, parameters, model }; + const res = await callAPI(hf, 'hf.zeroShotClassification', args); + return res; + }, + + /* + inputs = { + source_sentence: 'That is a happy person', + sentences: [ + 'That is a happy dog', + 'That is a very happy person', + 'Today is a sunny day' + ] + } + */ + + async sentenceSimilarity( + inputs, + model = defaultModels.sentenceSimilarity, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.sentenceSimilarity', args); + return res; + } +}); + +module.exports = { language }; diff --git a/lib/huggingface/utils/multimodal.js b/lib/huggingface/utils/multimodal.js new file mode 100644 index 0000000..cd94e86 --- /dev/null +++ b/lib/huggingface/utils/multimodal.js @@ -0,0 +1,53 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const defaultModels = DEFAULT_MODELS.multimodal; + +//......Multimodal....... + +const multimodal = (hf) => ({ + + // inputs = "That is a happy person", + async featureExtraction( + inputs, + model = defaultModels.featureExtraction, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.featureExtraction', args); + return res; + }, + + /* + inputs = { + question: 'How many cats are lying down?', + image: await (await fetch('https://placekitten.com/300/300')).blob() + }, + */ + async visualQuestionAnswering( + inputs, + model = defaultModels.visualQuestionAnswering, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.visualQuestionAnswering', args); + return res; + }, + + /* + inputs = { + question: 'Invoice number?', + image: await (await fetch('https://huggingface.co/spaces/impira/docquery/resolve/2359223c1837a7587402bda0f2643382a6eefeab/invoice.png')).blob(), + }, + */ + async documentQuestionAnswering( + inputs, + model = defaultModels.documentQuestionAnswering, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.documentQuestionAnswering', args); + return res; + } +}); + +module.exports = { multimodal }; diff --git a/lib/huggingface/utils/tabular.js b/lib/huggingface/utils/tabular.js new file mode 100644 index 0000000..a4f13ef --- /dev/null +++ b/lib/huggingface/utils/tabular.js @@ -0,0 +1,59 @@ +'use strict'; + +const { callAPI } = require('../../common.js'); +const { DEFAULT_MODELS } = require('../config.json'); + +const defaultModels = DEFAULT_MODELS.tabular; + +//......Tabular....... + +const tabular = (hf) => ({ + /* + inputs = { + data: { + "Height": ["11.52", "12.48", "12.3778"], + "Length1": ["23.2", "24", "23.9"], + "Length2": ["25.4", "26.3", "26.5"], + "Length3": ["30", "31.2", "31.1"], + "Species": ["Bream", "Bream", "Bream"], + "Width": ["4.02", "4.3056", "4.6961"] + }, + }, + */ + async tabularRegression( + inputs, + model = defaultModels.tabularRegression, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.tabularRegression', args); + return res; + }, + + /* + inputs = { + data: { + "fixed_acidity": ["7.4", "7.8", "10.3"], + "volatile_acidity": ["0.7", "0.88", "0.32"], + "citric_acid": ["0", "0", "0.45"], + "residual_sugar": ["1.9", "2.6", "6.4"], + "chlorides": ["0.076", "0.098", "0.073"], + "free_sulfur_dioxide": ["11", "25", "5"], + "total_sulfur_dioxide": ["34", "67", "13"], + "density": ["0.9978", "0.9968", "0.9976"], + "pH": ["3.51", "3.2", "3.23"], + "sulphates": ["0.56", "0.68", "0.82"], + "alcohol": ["9.4", "9.8", "12.6"] + }, + }, + */ + async tabularClassification( + inputs, + model = defaultModels.tabularClassification, + ) { + const args = { inputs, model }; + const res = await callAPI(hf, 'hf.tabularClassification', args); + return res; + } +}); + +module.exports = { tabular }; diff --git a/test/huggingface.js b/test/huggingface.js deleted file mode 100644 index 921ecd7..0000000 --- a/test/huggingface.js +++ /dev/null @@ -1,439 +0,0 @@ -'use strict'; - -const test = require('node:test'); -const assert = require('node:assert'); -const path = require('node:path'); -const { readFileSync } = require('node:fs'); -const { huggingface } = require('../lib'); - -const { - FillMask, - Summarization, - QuestionAnswering, - TableQuestionAnswering, - TextClassification, - TextGeneration, - TextGenerationStream, - TokenClassification, - Translation, - ZeroShotClassification, - SentenceSimilarity, - - AutomaticSpeechRecognition, - AudioClassification, - TextToSpeech, - // AudioToAudio, - - ImageClassification, - ObjectDetection, - ImageSegmentation, - ImageToText, - TextToImage, - ImageToImage, - ZeroShotImageClassification, - FeatureExtraction, - - VisualQuestionAnswering, - DocumentQuestionAnswering, - // TabularRegression, - // TabularClassification, - // CustomCall, - // CustomInferenceEndpoint, - CustomCallStreaming, -} = huggingface; - -const FILES = process.cwd() + '/files/huggingface/'; -const AUDIOS = FILES + 'audios'; -const IMAGES = FILES + 'images'; - -const testAudioFile = readFileSync(path.join(AUDIOS, 'speech.mp3')); -const testCatFile = readFileSync(path.join(IMAGES, 'cat.jpg')); -const testSeeFile = readFileSync(path.join(IMAGES, 'see.jpeg')); -const testInvoiceFile = readFileSync(path.join(IMAGES, 'invoice.png')); - -test('HuggingFace Connector', async (t) => { - await t.test('FillMask', async () => { - const masks = await FillMask('[MASK] world!'); - - assert.ok(Array.isArray(masks)); - - for (const mask of masks) { - assert.ok(typeof mask === 'object'); - assert.ok('score' in mask); - assert.ok('sequence' in mask); - assert.ok('token' in mask); - assert.ok('token_str' in mask); - } - }); - - await t.test('Summarization', async () => { - const res = await Summarization( - 'The tower is 324 metres (1,063 ft) tall about the same height as an' + - '81-storey building, and the tallest structure in Paris. Its base is' + - 'square, measuring 125 metres (410 ft) on each side. During its' + - 'construction, the Eiffel Tower surpassed the Washington Monument to' + - 'become the tallest', - ); - - assert.ok(typeof res === 'object'); - assert.ok('summary_text' in res); - }); - - await t.test('QuestionAnswering', async () => { - const res = await QuestionAnswering({ - question: 'What is the capital of France?', - context: 'The capital of France is Paris.', - }); - - assert.ok(typeof res === 'object'); - assert.ok('score' in res); - assert.ok('start' in res); - assert.ok('end' in res); - assert.ok('answer' in res); - assert.strictEqual(res.answer, 'Paris'); - }); - - await t.test('TableQuestionAnswering', async () => { - const res = await TableQuestionAnswering({ - query: 'How many stars does the transformers repository have?', - table: { - Repository: ['Transformers', 'Datasets', 'Tokenizers'], - Stars: ['36542', '4512', '3934'], - Contributors: ['651', '77', '34'], - 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'], - }, - }); - - assert.ok(typeof res === 'object'); - assert.ok('answer' in res); - assert.ok('coordinates' in res); - assert.ok('cells' in res); - assert.ok('aggregator' in res); - assert.deepEqual(res.coordinates, [[0, 1]]); - assert.strictEqual(res.aggregator, 'AVERAGE'); - }); - - await t.test('TextClassification', async () => { - const res = await TextClassification('I like you. I love you.'); - - assert.ok(Array.isArray(res)); - for (const item of res) { - assert.ok('label' in item); - assert.ok('score' in item); - } - }); - - await t.test('TextGeneration', async () => { - const res = await TextGeneration('The answer to the universe is'); - - assert.ok(typeof res === 'object'); - assert.ok('generated_text' in res); - }); - - await t.test('TextGenerationStream', async () => { - const res = await TextGenerationStream('repeat "one two three four"', { - max_new_tokens: 250, - }); - - assert.ok(typeof res === 'object'); - }); - - await t.test('TokenClassification', async () => { - const res = await TokenClassification( - 'My name is Sarah Jessica Parker but you can call me Jessica', - ); - - // console.log(res); - assert.ok(Array.isArray(res)); - - for (const item of res) { - assert.ok(typeof item === 'object'); - assert.ok('start' in item); - assert.ok('end' in item); - assert.ok('entity_group' in item); - assert.ok('score' in item); - assert.ok('word' in item); - } - }); - - await t.test('Translation', async () => { - const res = await Translation( - 'My name is Wolfgang and I live in Amsterdam', - { src_lang: 'en_XX', tgt_lang: 'fr_XX' }, - ); - - assert.ok(typeof res === 'object'); - assert.ok('translation_text' in res); - assert.ok(typeof res.translation_text === 'string'); - }); - - await t.test('ZeroShotClassification', async () => { - const res = await ZeroShotClassification( - [ - 'Hi, I recently bought a device from your company but' + - ' it is not working as advertised and I would like to' + - ' get reimbursed!', - ], - { - candidate_labels: ['refund', 'legal', 'faq'], - }, - ); - - assert.ok(Array.isArray(res)); - assert.ok(res.length === 1); - - const [item] = res; - - assert.ok(typeof item === 'object'); - assert.ok('sequence' in item); - assert.ok('labels' in item); - assert.ok('scores' in item); - assert.ok(typeof item.sequence === 'string'); - assert.ok(Array.isArray(item.labels)); - assert.ok(Array.isArray(item.scores)); - }); - - await t.test('SentenceSimilarity', async () => { - const res = await SentenceSimilarity({ - source_sentence: 'That is a happy person', - sentences: [ - 'That is a happy dog', - 'That is a very happy person', - 'Today is a sunny day', - ], - }); - - assert.ok(Array.isArray(res)); - assert.ok(res.every((item) => typeof item === 'number')); - }); - - await t.test('AutomaticSpeechRecognition', async () => { - const res = await AutomaticSpeechRecognition(testAudioFile); - - assert.ok(typeof res === 'object'); - assert.ok('text' in res); - assert.ok(typeof res.text === 'string'); - }); - - await t.test('AudioClassification', async () => { - const res = await AudioClassification(testAudioFile); - - assert.ok(Array.isArray(res)); - - for (const item of res) { - assert.ok(typeof item === 'object'); - assert.ok('label' in item); - assert.ok('score' in item); - } - }); - - await t.test('TextToSpeech', async () => { - const res = await TextToSpeech('Hello world!'); - - assert.ok(res instanceof Blob); - assert.ok(typeof res.size === 'number'); - assert.strictEqual(res.type, 'audio/flac'); - }); - - // TODO: fix test, getting an error "interface not in config.json" - // test.skip('AudioToAudio', async () => { - // const res = await AudioToAudio(testAudioFile); - // - // console.log(res); - // }); - - await t.test('ImageClassification', async () => { - const res = await ImageClassification(testCatFile); - - assert.ok(Array.isArray(res)); - - for (const item of res) { - assert.ok(typeof item === 'object'); - assert.ok('label' in item); - assert.ok('score' in item); - } - }); - - await t.test('ObjectDetection', async () => { - const res = await ObjectDetection(testCatFile); - - assert.ok(Array.isArray(res)); - assert.ok(res.length === 1); - - const [item] = res; - - assert.ok(typeof item === 'object'); - assert.ok('box' in item); - assert.ok('label' in item); - assert.ok('score' in item); - assert.ok(typeof item.box === 'object'); - assert.ok(typeof item.label === 'string'); - assert.ok(typeof item.score === 'number'); - assert.strictEqual(item.label, 'cat'); - - const { box } = item; - - assert.ok(typeof box === 'object'); - assert.ok(typeof box.xmin === 'number'); - assert.ok(typeof box.ymin === 'number'); - assert.ok(typeof box.xmax === 'number'); - assert.ok(typeof box.ymax === 'number'); - }); - - await t.test('ImageSegmentation', async () => { - const res = await ImageSegmentation(testCatFile); - - assert.ok(Array.isArray(res)); - - for (const item of res) { - assert.ok(typeof item === 'object'); - assert.ok('score' in item); - assert.ok('label' in item); - assert.ok('mask' in item); - } - }); - - await t.test('ImageToText', async () => { - const res = await ImageToText(new Blob([testSeeFile])); - - assert.ok(typeof res === 'object'); - assert.ok('generated_text' in res); - assert.ok(typeof res.generated_text === 'string'); - }); - - await t.test('TextToImage', async () => { - const inputs = - 'award winning high resolution photo of a giant tortoise' + - '/((ladybird)) hybrid, [trending on artstation]'; - const res = await TextToImage(inputs, { negative_prompt: 'blurry' }); - - assert.ok(res instanceof Blob); - assert.ok(typeof res.size === 'number'); - assert.ok(res.type === 'image/jpeg'); - }); - - await t.test('ImageToImage', async () => { - const res = await ImageToImage(new Blob([testSeeFile]), { - prompt: 'test picture', - }); - - assert.ok(res instanceof Blob); - assert.ok(typeof res.size === 'number'); - assert.ok(res.type === 'image/jpeg'); - }); - - await t.test('ZeroShotImageClassification', async () => { - const inputs = { image: new Blob([testCatFile]) }; - const res = await ZeroShotImageClassification(inputs, { - candidate_labels: ['cat', 'dog'], - }); - - assert.ok(Array.isArray(res)); - - for (const item of res) { - assert.ok(typeof item === 'object'); - assert.ok('score' in item); - assert.ok('label' in item); - } - }); - - await t.test('FeatureExtraction', async () => { - const res = await FeatureExtraction('That is a happy person'); - - assert.ok(Array.isArray(res)); - assert.ok(res.every((el) => typeof el === 'number')); - }); - - await t.test('VisualQuestionAnswering', async () => { - const inputs = { - question: 'How many cats are lying down?', - image: new Blob([testCatFile]), - }; - const res = await VisualQuestionAnswering(inputs); - - assert.ok(typeof res === 'object'); - assert.ok('score' in res); - assert.ok('answer' in res); - assert.ok(typeof res.score === 'number'); - assert.strictEqual(res.answer, '1'); - }); - - await t.test('DocumentQuestionAnswering', async () => { - const inputs = { - question: 'Invoice number?', - image: new Blob([testInvoiceFile]), - }; - const res = await DocumentQuestionAnswering(inputs); - - assert.ok(typeof res === 'object'); - assert.ok('score' in res && typeof res.score === 'number'); - assert.ok('start' in res && typeof res.start === 'number'); - assert.ok('end' in res && typeof res.end === 'number'); - assert.ok('answer' in res && typeof res.answer === 'string'); - assert.strictEqual(res.answer, 'us-001'); - }); - - // TODO: fix test, timeout - // test.skip('TabularRegression', async () => { - // const inputs = { - // data: { - // Height: ['11.52', '12.48', '12.3778'], - // Length1: ['23.2', '24', '23.9'], - // Length2: ['25.4', '26.3', '26.5'], - // Length3: ['30', '31.2', '31.1'], - // Species: ['Bream', 'Bream', 'Bream'], - // Width: ['4.02', '4.3056', '4.6961'], - // }, - // }; - // const res = await TabularRegression(inputs); - // - // console.log(res); - // }, 60000); - - // TODO: fix test, timeout - // test.skip('TabularClassification', async () => { - // const inputs = { - // data: { - // fixed_acidity: ['7.4', '7.8', '10.3'], - // volatile_acidity: ['0.7', '0.88', '0.32'], - // citric_acid: ['0', '0', '0.45'], - // residual_sugar: ['1.9', '2.6', '6.4'], - // chlorides: ['0.076', '0.098', '0.073'], - // free_sulfur_dioxide: ['11', '25', '5'], - // total_sulfur_dioxide: ['34', '67', '13'], - // density: ['0.9978', '0.9968', '0.9976'], - // pH: ['3.51', '3.2', '3.23'], - // sulphates: ['0.56', '0.68', '0.82'], - // alcohol: ['9.4', '9.8', '12.6'], - // }, - // }; - // const res = await TabularClassification(inputs); - // - // console.log(res); - // }, 60000); - - // TODO: fix test, response is undefined for some reason - // test.skip('CustomCall', async () => { - // const res = await CustomCall('hello world'); - // - // console.log(res); - // }); - - await t.test('CustomCallStreaming', async () => { - const res = await CustomCallStreaming('hello world'); - - assert.ok(typeof res === 'object'); - }); - - // TODO: To test this one we need to have own inference endpoint - // test.skip('CustomInferenceEndpoint', async () => { - // const endpoint = - // 'https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2'; - // const res = await CustomInferenceEndpoint( - // 'The answer to the universe is', - // endpoint, - // ); - // - // console.log(res); - // }); -}); diff --git a/test/huggingface/audio.js b/test/huggingface/audio.js new file mode 100644 index 0000000..5ce7bfe --- /dev/null +++ b/test/huggingface/audio.js @@ -0,0 +1,66 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); +const { readFileSync } = require('node:fs'); + +const { huggingface } = require('../../lib'); +const utils = require('../../lib/huggingface/utils'); + +const { Chat } = huggingface; + +const API_KEY = process.env.HUGGINGFACE_API_KEY; + +const { + audio: uAudio, +} = utils; + +const FILES = process.cwd() + '/files/huggingface/'; +const AUDIOS = FILES + 'audios'; + +const testAudioFile = readFileSync(path.join(AUDIOS, 'speech.mp3')); + +describe('audio', () => { + let audio; + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + audio = uAudio(chat.hf); + }); + + it('automaticSpeechRecognition', async () => { + const res = await audio.automaticSpeechRecognition(testAudioFile); + + console.log(res); + assert.ok(typeof res === 'object'); + assert.ok('text' in res); + assert.ok(typeof res.text === 'string'); + }); + + it('audioClassification', async () => { + const res = await audio.audioClassification(testAudioFile); + + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('label' in item); + assert.ok('score' in item); + } + }); + + it('textToSpeech', async () => { + const res = await audio.textToSpeech('Hello world!'); + + assert.ok(res instanceof Blob); + assert.ok(typeof res.size === 'number'); + assert.strictEqual(res.type, 'audio/flac'); + }); + + // TODO: fix test, getting an error "interface not in config.json" + it.skip('audioToAudio', async () => { + const res = await audio.audioToAudio(testAudioFile); + + console.log(res); + }); +}); diff --git a/test/huggingface/computerVision.js b/test/huggingface/computerVision.js new file mode 100644 index 0000000..50ed7f1 --- /dev/null +++ b/test/huggingface/computerVision.js @@ -0,0 +1,129 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); +const { readFileSync } = require('node:fs'); + +const { huggingface } = require('../../lib'); +const utils = require('../../lib/huggingface/utils'); + +const { Chat } = huggingface; + +const API_KEY = process.env.HUGGINGFACE_API_KEY; + +const { + computerVision: uComputerVision, +} = utils; + +const FILES = process.cwd() + '/files/huggingface/'; +const IMAGES = FILES + 'images'; + +const testCatFile = readFileSync(path.join(IMAGES, 'cat.jpg')); +const testSeeFile = readFileSync(path.join(IMAGES, 'see.jpeg')); + +describe('computerVision', () => { + let computerVision; + + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + computerVision = uComputerVision(chat.hf); + }); + + it('imageClassification', async () => { + const res = await computerVision.imageClassification(testCatFile); + + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('label' in item); + assert.ok('score' in item); + } + }); + + it('objectDetection', async () => { + const res = await computerVision.objectDetection(testCatFile); + + assert.ok(Array.isArray(res)); + assert.ok(res.length === 1); + + const [item] = res; + + assert.ok(typeof item === 'object'); + assert.ok('box' in item); + assert.ok('label' in item); + assert.ok('score' in item); + assert.ok(typeof item.box === 'object'); + assert.ok(typeof item.label === 'string'); + assert.ok(typeof item.score === 'number'); + assert.strictEqual(item.label, 'cat'); + + const { box } = item; + + assert.ok(typeof box === 'object'); + assert.ok(typeof box.xmin === 'number'); + assert.ok(typeof box.ymin === 'number'); + assert.ok(typeof box.xmax === 'number'); + assert.ok(typeof box.ymax === 'number'); + }); + + it('imageSegmentation', async () => { + const res = await computerVision.imageSegmentation(testCatFile); + + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('score' in item); + assert.ok('label' in item); + assert.ok('mask' in item); + } + }); + + it('imageToText', async () => { + const res = await computerVision.imageToText(testCatFile); + + assert.ok(typeof res === 'object'); + assert.ok('generated_text' in res); + assert.ok(typeof res.generated_text === 'string'); + }); + + it('textToImage', async () => { + const inputs = + 'award winning high resolution photo of a giant tortoise' + + '/((ladybird)) hybrid, [trending on artstation]'; + const res = await computerVision.textToImage(inputs, { + negative_prompt: 'blurry' + }); + + assert.ok(res instanceof Blob); + assert.ok(typeof res.size === 'number'); + assert.ok(res.type === 'image/jpeg'); + }); + + it('imageToImage', async () => { + const res = await computerVision.imageToImage(new Blob([testSeeFile]), { + prompt: 'test picture', + }); + + assert.ok(res instanceof Blob); + assert.ok(typeof res.size === 'number'); + assert.ok(res.type === 'image/jpeg'); + }); + + it('ZeroShotImageClassification', async () => { + const inputs = { image: new Blob([testCatFile]) }; + const res = await computerVision.zeroShotImageClassification(inputs, { + candidate_labels: ['cat', 'dog'], + }); + + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('score' in item); + assert.ok('label' in item); + } + }); +}); diff --git a/test/huggingface/custom.js b/test/huggingface/custom.js new file mode 100644 index 0000000..82f170a --- /dev/null +++ b/test/huggingface/custom.js @@ -0,0 +1,49 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); + +const { huggingface } = require('../../lib'); +const utils = require('../../lib/huggingface/utils'); + +const { Chat } = huggingface; + +const API_KEY = process.env.HUGGINGFACE_API_KEY; + +const { + custom: uCustom, +} = utils; + +describe('custom', () => { + let custom; + + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + custom = uCustom(chat.hf); + }); + + // TODO: fix test, response is undefined for some reason + it.skip('customCall', async () => { + const res = await custom.customCall('hello world'); + + console.log(res); + }); + + it('customCallStreaming', async () => { + const res = await custom.customCallStreaming('hello world'); + + assert.ok(typeof res === 'object'); + }); + + // TODO: To test this one we need to have own inference endpoint + it.skip('customInferenceEndpoint', async () => { + const endpoint = + 'https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2'; + const res = await custom.customInferenceEndpoint( + 'The answer to the universe is', + endpoint, + ); + + console.log(res); + }); +}); diff --git a/test/huggingface/language.js b/test/huggingface/language.js new file mode 100644 index 0000000..8e8b168 --- /dev/null +++ b/test/huggingface/language.js @@ -0,0 +1,181 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); + +const { huggingface } = require('../../lib'); +const utils = require('../../lib/huggingface/utils'); + +const { Chat } = huggingface; + +const API_KEY = process.env.HUGGINGFACE_API_KEY; + +const { + language: uLanguage, +} = utils; + +describe('language', () => { + let language; + + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + language = uLanguage(chat.hf); + }); + + it('fillMask', async () => { + const masks = await language.fillMask('[MASK] world!'); + + assert.ok(Array.isArray(masks)); + + for (const mask of masks) { + assert.ok(typeof mask === 'object'); + assert.ok('score' in mask); + assert.ok('sequence' in mask); + assert.ok('token' in mask); + assert.ok('token_str' in mask); + } + }); + + it('summarization', async () => { + const res = await language.summarization( + 'The tower is 324 metres (1,063 ft) tall about the same height as an' + + '81-storey building, and the tallest structure in Paris. Its base is' + + 'square, measuring 125 metres (410 ft) on each side. During its' + + 'construction, the Eiffel Tower surpassed the Washington Monument to' + + 'become the tallest', + ); + + assert.ok(typeof res === 'object'); + assert.ok('summary_text' in res); + }); + + it('questionAnswering', async () => { + const res = await language.questionAnswering({ + question: 'What is the capital of France?', + context: 'The capital of France is Paris.', + }); + + assert.ok(typeof res === 'object'); + assert.ok('score' in res); + assert.ok('start' in res); + assert.ok('end' in res); + assert.ok('answer' in res); + assert.strictEqual(res.answer, 'Paris'); + }); + + it('tableQuestionAnswering', async () => { + const res = await language.tableQuestionAnswering({ + query: 'How many stars does the transformers repository have?', + table: { + Repository: ['Transformers', 'Datasets', 'Tokenizers'], + Stars: ['36542', '4512', '3934'], + Contributors: ['651', '77', '34'], + 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'], + }, + }); + + assert.ok(typeof res === 'object'); + assert.ok('answer' in res); + assert.ok('coordinates' in res); + assert.ok('cells' in res); + assert.ok('aggregator' in res); + assert.deepEqual(res.coordinates, [[0, 1]]); + assert.strictEqual(res.aggregator, 'AVERAGE'); + }); + + it('textClassification', async () => { + const res = await language.textClassification('I like you. I love you.'); + + assert.ok(Array.isArray(res)); + for (const item of res) { + assert.ok('label' in item); + assert.ok('score' in item); + } + }); + + it('textGeneration', async () => { + const input = 'The answer to the universe is'; + const res = await language.textGeneration(input); + + assert.ok(typeof res === 'object'); + assert.ok('generated_text' in res); + }); + + it('textGenerationStream', async () => { + const input = 'repeat "one two three four"'; + const res = await language.textGenerationStream(input, { + max_new_tokens: 250, + }); + + assert.ok(typeof res === 'object'); + }); + + it('tokenClassification', async () => { + const res = await language.tokenClassification( + 'My name is Sarah Jessica Parker but you can call me Jessica', + ); + + // console.log(res); + assert.ok(Array.isArray(res)); + + for (const item of res) { + assert.ok(typeof item === 'object'); + assert.ok('start' in item); + assert.ok('end' in item); + assert.ok('entity_group' in item); + assert.ok('score' in item); + assert.ok('word' in item); + } + }); + + it('translation', async () => { + const res = await language.translation( + 'My name is Wolfgang and I live in Amsterdam', + { src_lang: 'en_XX', tgt_lang: 'fr_XX' }, + ); + + assert.ok(typeof res === 'object'); + assert.ok('translation_text' in res); + assert.ok(typeof res.translation_text === 'string'); + }); + + it('zeroShotClassification', async () => { + const res = await language.zeroShotClassification( + [ + 'Hi, I recently bought a device from your company but' + + ' it is not working as advertised and I would like to' + + ' get reimbursed!', + ], + { + candidate_labels: ['refund', 'legal', 'faq'], + }, + ); + + assert.ok(Array.isArray(res)); + assert.ok(res.length === 1); + + const [item] = res; + + assert.ok(typeof item === 'object'); + assert.ok('sequence' in item); + assert.ok('labels' in item); + assert.ok('scores' in item); + assert.ok(typeof item.sequence === 'string'); + assert.ok(Array.isArray(item.labels)); + assert.ok(Array.isArray(item.scores)); + }); + + it('sentenceSimilarity', async () => { + const res = await language.sentenceSimilarity({ + source_sentence: 'That is a happy person', + sentences: [ + 'That is a happy dog', + 'That is a very happy person', + 'Today is a sunny day', + ], + }); + + assert.ok(Array.isArray(res)); + assert.ok(res.every((item) => typeof item === 'number')); + }); +}); diff --git a/test/huggingface/multimodal.js b/test/huggingface/multimodal.js new file mode 100644 index 0000000..85212dc --- /dev/null +++ b/test/huggingface/multimodal.js @@ -0,0 +1,68 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); +const { readFileSync } = require('node:fs'); + +const { huggingface } = require('../../lib'); +const utils = require('../../lib/huggingface/utils'); + +const { Chat } = huggingface; + +const API_KEY = process.env.HUGGINGFACE_API_KEY; + +const { + multimodal: uMultimodal, +} = utils; + +const FILES = process.cwd() + '/files/huggingface/'; +const IMAGES = FILES + 'images'; + +const testCatFile = readFileSync(path.join(IMAGES, 'cat.jpg')); +const testInvoiceFile = readFileSync(path.join(IMAGES, 'invoice.png')); + +describe('multimodal', () => { + let multimodal; + + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + multimodal = uMultimodal(chat.hf); + }); + + it('featureExtraction', async () => { + const res = await multimodal.featureExtraction('That is a happy person'); + + assert.ok(Array.isArray(res)); + assert.ok(res.every((el) => typeof el === 'number')); + }); + + it('visualQuestionAnswering', async () => { + const inputs = { + question: 'How many cats are lying down?', + image: new Blob([testCatFile]), + }; + const res = await multimodal.visualQuestionAnswering(inputs); + + assert.ok(typeof res === 'object'); + assert.ok('score' in res); + assert.ok('answer' in res); + assert.ok(typeof res.score === 'number'); + assert.strictEqual(res.answer, '1'); + }); + + it('documentQuestionAnswering', async () => { + const inputs = { + question: 'Invoice number?', + image: new Blob([testInvoiceFile]), + }; + const res = await multimodal.documentQuestionAnswering(inputs); + + assert.ok(typeof res === 'object'); + assert.ok('score' in res && typeof res.score === 'number'); + assert.ok('start' in res && typeof res.start === 'number'); + assert.ok('end' in res && typeof res.end === 'number'); + assert.ok('answer' in res && typeof res.answer === 'string'); + assert.strictEqual(res.answer, 'us-001'); + }); +}); diff --git a/test/huggingface/tabular.js b/test/huggingface/tabular.js new file mode 100644 index 0000000..f5110b0 --- /dev/null +++ b/test/huggingface/tabular.js @@ -0,0 +1,62 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); + +const { huggingface } = require('../../lib'); +const utils = require('../../lib/huggingface/utils'); + +const { Chat } = huggingface; + +const API_KEY = process.env.HUGGINGFACE_API_KEY; + +const { + tabular: uTabular, +} = utils; + +describe('tabular', () => { + let tabular; + + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + tabular = uTabular(chat.hf); + }); + + // TODO: fix test, timeout + it.skip('TabularRegression', async () => { + const inputs = { + data: { + Height: ['11.52', '12.48', '12.3778'], + Length1: ['23.2', '24', '23.9'], + Length2: ['25.4', '26.3', '26.5'], + Length3: ['30', '31.2', '31.1'], + Species: ['Bream', 'Bream', 'Bream'], + Width: ['4.02', '4.3056', '4.6961'], + }, + }; + const res = await tabular.tabularRegression(inputs); + + console.log(res); + }, 60000); + + // TODO: fix test, timeout + it.skip('TabularClassification', async () => { + const inputs = { + data: { + fixed_acidity: ['7.4', '7.8', '10.3'], + volatile_acidity: ['0.7', '0.88', '0.32'], + citric_acid: ['0', '0', '0.45'], + residual_sugar: ['1.9', '2.6', '6.4'], + chlorides: ['0.076', '0.098', '0.073'], + free_sulfur_dioxide: ['11', '25', '5'], + total_sulfur_dioxide: ['34', '67', '13'], + density: ['0.9978', '0.9968', '0.9976'], + pH: ['3.51', '3.2', '3.23'], + sulphates: ['0.56', '0.68', '0.82'], + alcohol: ['9.4', '9.8', '12.6'], + }, + }; + const res = await tabular.TabularClassification(inputs); + + console.log(res); + }, 60000); +}); From 18eb318edb6bded25b9100df14f97970d0cf23b8 Mon Sep 17 00:00:00 2001 From: svmlitimur Date: Thu, 16 May 2024 16:54:23 +0300 Subject: [PATCH 3/5] Fix style, remove useless comments and formatting --- lib/huggingface/utils/audio.js | 15 +++---------- lib/huggingface/utils/computerVision.js | 17 +++------------ lib/huggingface/utils/custom.js | 19 +++++------------ lib/huggingface/utils/language.js | 28 +++++++------------------ lib/huggingface/utils/multimodal.js | 10 ++------- lib/huggingface/utils/tabular.js | 9 ++------ test/huggingface/audio.js | 4 +--- test/huggingface/computerVision.js | 10 ++++----- test/huggingface/custom.js | 4 +--- test/huggingface/language.js | 4 +--- test/huggingface/multimodal.js | 4 +--- test/huggingface/tabular.js | 4 +--- 12 files changed, 32 insertions(+), 96 deletions(-) diff --git a/lib/huggingface/utils/audio.js b/lib/huggingface/utils/audio.js index 34d0597..7d07375 100644 --- a/lib/huggingface/utils/audio.js +++ b/lib/huggingface/utils/audio.js @@ -5,10 +5,7 @@ const { DEFAULT_MODELS } = require('../config.json'); const defaultModels = DEFAULT_MODELS.audio; -//......Audio....... - const audio = (hf) => ({ - // data = readFileSync('test/sample1.flac') async automaticSpeechRecognition( data, @@ -20,20 +17,14 @@ const audio = (hf) => ({ }, // data = readFileSync('test/sample1.flac') - async audioClassification( - data, - model = defaultModels.audioClassification, - ) { + async audioClassification(data, model = defaultModels.audioClassification) { const args = { data, model }; const res = await callAPI(hf, 'hf.audioClassification', args); return res; }, // inputs = 'Hello world!' - async textToSpeech( - inputs, - model = defaultModels.textToSpeech, - ) { + async textToSpeech(inputs, model = defaultModels.textToSpeech) { const args = { inputs, model }; const res = await callAPI(hf, 'hf.textToSpeech', args); return res; @@ -44,7 +35,7 @@ const audio = (hf) => ({ const args = { data, model }; const res = await callAPI(hf, 'hf.audioToAudio', args); return res; - } + }, }); module.exports = { audio }; diff --git a/lib/huggingface/utils/computerVision.js b/lib/huggingface/utils/computerVision.js index 63655ad..97c7db7 100644 --- a/lib/huggingface/utils/computerVision.js +++ b/lib/huggingface/utils/computerVision.js @@ -5,14 +5,9 @@ const { DEFAULT_MODELS } = require('../config.json'); const defaultModels = DEFAULT_MODELS.computerVision; -//......ComputerVision....... - const computerVision = (hf) => ({ // data = readFileSync('test/cheetah.png') - async imageClassification( - data, - model = defaultModels.imageClassification, - ) { + async imageClassification(data, model = defaultModels.imageClassification) { const args = { data, model }; const res = await callAPI(hf, 'hf.imageClassification', args); return res; @@ -28,20 +23,14 @@ const computerVision = (hf) => ({ }, // data = readFileSync('test/cats.png') - async imageSegmentation( - data, - model = defaultModels.imageSegmentation, - ) { + async imageSegmentation(data, model = defaultModels.imageSegmentation) { const args = { data, model }; const res = await callAPI(hf, 'hf.imageSegmentation', args); return res; }, // data = await (await fetch('https://picsum.photos/300/300')).blob() - async imageToText( - data, - model = defaultModels.imageToText, - ) { + async imageToText(data, model = defaultModels.imageToText) { const args = { data, model }; const res = await callAPI(hf, 'hf.imageToText', args); return res; diff --git a/lib/huggingface/utils/custom.js b/lib/huggingface/utils/custom.js index 95dc5d5..831900d 100644 --- a/lib/huggingface/utils/custom.js +++ b/lib/huggingface/utils/custom.js @@ -2,8 +2,6 @@ const { callAPI } = require('../../common.js'); -//......Custom....... - const custom = (hf) => ({ /* inputs = "hello world", @@ -11,11 +9,7 @@ const custom = (hf) => ({ custom_param: 'some magic', } */ - async customCall( - inputs, - parameters = {}, - model, - ) { + async customCall(inputs, parameters = {}, model) { const args = { inputs, parameters, model }; const res = await callAPI(hf, 'hf.request', args); return res; @@ -27,11 +21,7 @@ const custom = (hf) => ({ custom_param: 'some magic', } */ - async customCallStreaming( - inputs, - parameters = {}, - model, - ) { + async customCallStreaming(inputs, parameters = {}, model) { const args = { inputs, parameters, model }; return callAPI(hf, 'hf.streamingRequest', args); }, @@ -51,9 +41,10 @@ const custom = (hf) => ({ 'Error occured while triggering "textGeneration" method', { cause: err, - }); + }, + ); } - } + }, }); module.exports = { custom }; diff --git a/lib/huggingface/utils/language.js b/lib/huggingface/utils/language.js index d6340dc..ba5e21d 100644 --- a/lib/huggingface/utils/language.js +++ b/lib/huggingface/utils/language.js @@ -5,8 +5,6 @@ const { DEFAULT_MODELS } = require('../config.json'); const defaultModels = DEFAULT_MODELS.language; -//......Natural Language Processing (language)....... - const language = (hf) => ({ // inputs = '[MASK] world!' async fillMask(inputs, model = defaultModels.fillMask) { @@ -39,10 +37,7 @@ const language = (hf) => ({ context: 'The capital of France is Paris.' }, */ - async questionAnswering( - inputs, - model = defaultModels.questionAnswering, - ) { + async questionAnswering(inputs, model = defaultModels.questionAnswering) { const args = { inputs, model }; const res = await callAPI(hf, 'hf.questionAnswering', args); return res; @@ -69,10 +64,7 @@ const language = (hf) => ({ }, // inputs = 'I like you. I love you.' - async textClassification( - inputs, - model = defaultModels.textClassification, - ) { + async textClassification(inputs, model = defaultModels.textClassification) { const args = { inputs, model }; const res = await callAPI(hf, 'hf.textClassification', args); return res; @@ -98,10 +90,7 @@ const language = (hf) => ({ }, // inputs = 'My name is Sarah Jessica Parker but you can call me Jessica' - async tokenClassification( - inputs, - model = defaultModels.tokenClassification, - ) { + async tokenClassification(inputs, model = defaultModels.tokenClassification) { const args = { inputs, model }; const res = await callAPI(hf, 'hf.tokenClassification', args); return res; @@ -110,7 +99,9 @@ const language = (hf) => ({ // inputs = 'My name is Wolfgang and I live in Amsterdam', // parameters = {"src_lang": "en_XX", "tgt_lang": "fr_XX"} async translation( - inputs, parameters = {}, model = defaultModels.translation + inputs, + parameters = {}, + model = defaultModels.translation, ) { const args = { inputs, parameters, model }; const res = await callAPI(hf, 'hf.translation', args); @@ -145,14 +136,11 @@ const language = (hf) => ({ } */ - async sentenceSimilarity( - inputs, - model = defaultModels.sentenceSimilarity, - ) { + async sentenceSimilarity(inputs, model = defaultModels.sentenceSimilarity) { const args = { inputs, model }; const res = await callAPI(hf, 'hf.sentenceSimilarity', args); return res; - } + }, }); module.exports = { language }; diff --git a/lib/huggingface/utils/multimodal.js b/lib/huggingface/utils/multimodal.js index cd94e86..6661493 100644 --- a/lib/huggingface/utils/multimodal.js +++ b/lib/huggingface/utils/multimodal.js @@ -5,15 +5,9 @@ const { DEFAULT_MODELS } = require('../config.json'); const defaultModels = DEFAULT_MODELS.multimodal; -//......Multimodal....... - const multimodal = (hf) => ({ - // inputs = "That is a happy person", - async featureExtraction( - inputs, - model = defaultModels.featureExtraction, - ) { + async featureExtraction(inputs, model = defaultModels.featureExtraction) { const args = { inputs, model }; const res = await callAPI(hf, 'hf.featureExtraction', args); return res; @@ -47,7 +41,7 @@ const multimodal = (hf) => ({ const args = { inputs, model }; const res = await callAPI(hf, 'hf.documentQuestionAnswering', args); return res; - } + }, }); module.exports = { multimodal }; diff --git a/lib/huggingface/utils/tabular.js b/lib/huggingface/utils/tabular.js index a4f13ef..1992101 100644 --- a/lib/huggingface/utils/tabular.js +++ b/lib/huggingface/utils/tabular.js @@ -5,8 +5,6 @@ const { DEFAULT_MODELS } = require('../config.json'); const defaultModels = DEFAULT_MODELS.tabular; -//......Tabular....... - const tabular = (hf) => ({ /* inputs = { @@ -20,10 +18,7 @@ const tabular = (hf) => ({ }, }, */ - async tabularRegression( - inputs, - model = defaultModels.tabularRegression, - ) { + async tabularRegression(inputs, model = defaultModels.tabularRegression) { const args = { inputs, model }; const res = await callAPI(hf, 'hf.tabularRegression', args); return res; @@ -53,7 +48,7 @@ const tabular = (hf) => ({ const args = { inputs, model }; const res = await callAPI(hf, 'hf.tabularClassification', args); return res; - } + }, }); module.exports = { tabular }; diff --git a/test/huggingface/audio.js b/test/huggingface/audio.js index 5ce7bfe..714cf72 100644 --- a/test/huggingface/audio.js +++ b/test/huggingface/audio.js @@ -12,9 +12,7 @@ const { Chat } = huggingface; const API_KEY = process.env.HUGGINGFACE_API_KEY; -const { - audio: uAudio, -} = utils; +const { audio: uAudio } = utils; const FILES = process.cwd() + '/files/huggingface/'; const AUDIOS = FILES + 'audios'; diff --git a/test/huggingface/computerVision.js b/test/huggingface/computerVision.js index 50ed7f1..3ee6587 100644 --- a/test/huggingface/computerVision.js +++ b/test/huggingface/computerVision.js @@ -12,9 +12,7 @@ const { Chat } = huggingface; const API_KEY = process.env.HUGGINGFACE_API_KEY; -const { - computerVision: uComputerVision, -} = utils; +const { computerVision: uComputerVision } = utils; const FILES = process.cwd() + '/files/huggingface/'; const IMAGES = FILES + 'images'; @@ -91,10 +89,10 @@ describe('computerVision', () => { it('textToImage', async () => { const inputs = - 'award winning high resolution photo of a giant tortoise' + - '/((ladybird)) hybrid, [trending on artstation]'; + 'award winning high resolution photo of a giant tortoise' + + '/((ladybird)) hybrid, [trending on artstation]'; const res = await computerVision.textToImage(inputs, { - negative_prompt: 'blurry' + negative_prompt: 'blurry', }); assert.ok(res instanceof Blob); diff --git a/test/huggingface/custom.js b/test/huggingface/custom.js index 82f170a..992aca4 100644 --- a/test/huggingface/custom.js +++ b/test/huggingface/custom.js @@ -10,9 +10,7 @@ const { Chat } = huggingface; const API_KEY = process.env.HUGGINGFACE_API_KEY; -const { - custom: uCustom, -} = utils; +const { custom: uCustom } = utils; describe('custom', () => { let custom; diff --git a/test/huggingface/language.js b/test/huggingface/language.js index 8e8b168..017eaec 100644 --- a/test/huggingface/language.js +++ b/test/huggingface/language.js @@ -10,9 +10,7 @@ const { Chat } = huggingface; const API_KEY = process.env.HUGGINGFACE_API_KEY; -const { - language: uLanguage, -} = utils; +const { language: uLanguage } = utils; describe('language', () => { let language; diff --git a/test/huggingface/multimodal.js b/test/huggingface/multimodal.js index 85212dc..74034e5 100644 --- a/test/huggingface/multimodal.js +++ b/test/huggingface/multimodal.js @@ -12,9 +12,7 @@ const { Chat } = huggingface; const API_KEY = process.env.HUGGINGFACE_API_KEY; -const { - multimodal: uMultimodal, -} = utils; +const { multimodal: uMultimodal } = utils; const FILES = process.cwd() + '/files/huggingface/'; const IMAGES = FILES + 'images'; diff --git a/test/huggingface/tabular.js b/test/huggingface/tabular.js index f5110b0..0a65bf9 100644 --- a/test/huggingface/tabular.js +++ b/test/huggingface/tabular.js @@ -9,9 +9,7 @@ const { Chat } = huggingface; const API_KEY = process.env.HUGGINGFACE_API_KEY; -const { - tabular: uTabular, -} = utils; +const { tabular: uTabular } = utils; describe('tabular', () => { let tabular; From 72637929e2f969bc43c05140c27462e4ac75738b Mon Sep 17 00:00:00 2001 From: Timur Sevimli Date: Thu, 16 May 2024 22:18:27 +0300 Subject: [PATCH 4/5] Temporary update CI --- .github/workflows/test.yml | 1 - .gitignore | 2 -- package.json | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9302e5a..56d6ae8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,6 @@ on: jobs: build: - if: false runs-on: ${{ matrix.os }} strategy: matrix: diff --git a/.gitignore b/.gitignore index 2429465..fd8ef83 100644 --- a/.gitignore +++ b/.gitignore @@ -131,8 +131,6 @@ dist -#main.js is for local testing only -main.js #mac .DS_Store .DS_Store? diff --git a/package.json b/package.json index 43394fb..afce835 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "readmeFilename": "README.md", "scripts": { - "test": "npm run lint && npm run types && node --env-file=.env --test --test-timeout 60000", + "test": "npm run lint", "types": "tsc -p tsconfig.json", "lint": "eslint . && prettier --check \"**/*.js\" \"**/*.json\" \"**/*.md\" \"**/.*rc\"", "fmt": "prettier --write \"**/*.js\" \"**/*.json\" \"**/*.md\" \"**/.*rc\"" From 4e579306bf5e2d25084f6d2c02e4b558166344c3 Mon Sep 17 00:00:00 2001 From: svmlitimur Date: Fri, 17 May 2024 01:18:08 +0300 Subject: [PATCH 5/5] Update OpenAI tests style and structure --- test/openai.js | 403 ------------------------------------- test/openai/chat.js | 40 ++++ test/openai/files.js | 88 ++++++++ test/openai/finetune.js | 62 ++++++ test/openai/images.js | 60 ++++++ test/openai/language.js | 61 ++++++ test/openai/models.js | 45 +++++ test/openai/recognition.js | 31 +++ test/openai/speech.js | 62 ++++++ test/openai/tested.js | 28 +++ 10 files changed, 477 insertions(+), 403 deletions(-) delete mode 100644 test/openai.js create mode 100644 test/openai/chat.js create mode 100644 test/openai/files.js create mode 100644 test/openai/finetune.js create mode 100644 test/openai/images.js create mode 100644 test/openai/language.js create mode 100644 test/openai/models.js create mode 100644 test/openai/recognition.js create mode 100644 test/openai/speech.js create mode 100644 test/openai/tested.js diff --git a/test/openai.js b/test/openai.js deleted file mode 100644 index abe3919..0000000 --- a/test/openai.js +++ /dev/null @@ -1,403 +0,0 @@ -'use strict'; - -const test = require('node:test'); -const assert = require('node:assert'); -const fs = require('node:fs'); -const path = require('node:path'); -const { openai } = require('../lib'); -const utils = require('../lib/openai/utils'); -const common = require('../lib/common.js'); - -const { Chat } = openai; -const API_KEY = process.env.OPENAI_API_KEY; - -const FILES = path.join(__dirname, '../files'); -const OPEN_AI_FILES = path.join(FILES, 'openai'); -const AUDIOS = path.join(OPEN_AI_FILES, 'audios'); -const IMAGES = path.join(OPEN_AI_FILES, 'images'); -const FINE_TUNE = path.join(OPEN_AI_FILES, 'fine-tune'); -const ASSISTANTS = path.join(OPEN_AI_FILES, 'assistants'); -const TOOLS = path.join(OPEN_AI_FILES, 'tools'); -const TEST_LIBRARY = require(TOOLS + '/test-library.js'); - -const { - language, - files, - fineTune, - models, - images, - speech, - recognition, - // tokens, - // assistants, -} = utils; - -const tested = { - chat: new Chat({}), - fineTune: { - id: '', - file: { id: '' }, - }, - model: { - id: '', - }, - assistant: { - id: '', - thread: { id: '' }, - message: { id: '' }, - run: { id: '' }, - step: { id: '' }, - file: { id: '' }, - added_file: { id: '' }, - }, -}; -// const clean = async () => {}; -// const file_id = 'file-X4ZR3WmEyxJKOEuyuthIIj9T'; -// const ftJobId = 'ftjob-WUootDAvgqK1iJlpTn1XZmoO'; -const modelId = 'gpt-3.5-turbo-16k'; - -test('Chat message', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await chat.message({ text: 'Hello' }); - - assert.strictEqual(typeof res, 'string'); -}); - -test('Chat voice message', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await chat.voiceMessage({ - inputFilePath: AUDIOS + '/test-speech-input-en.mp3', - outputFilePath: AUDIOS + '/my_test-speech-output-en.mp3', - returnIntermediateResult: false, - }); - - assert.ok('inputText' in res); - assert.strictEqual(res.inputText, 'Hello there.'); - assert.ok('outputText' in res); - assert.ok('outputFilePath' in res); -}); - -test('Text completion', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await language(chat.openai).generate({ text: 'hello' }); - - assert.ok('message' in res); - assert.ok('messages' in res); - assert.ok('usage' in res); -}); - -test('Text completeon with function call', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await language(chat.openai).generate({ - text: 'What is the weather like in San Francisco, Tokyo and Paris?', - tools: TEST_LIBRARY.tools, - }); - - assert.ok('message' in res); - assert.ok('messages' in res); - assert.ok('usage' in res); -}); - -test('Text Embedding', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await language(chat.openai).embedding({ text: 'hello' }); - - assert.ok('embedding' in res); - assert.ok('usage' in res); -}); - -// WARN: 400 Invalid value for 'model' = text-moderation-007. - -// await t.test('Text Classification', async () => { -// const chat = new Chat({ apiKey: API_KEY }); -// -// const res = await language(chat.openai).classification({ -// text: 'I will kill you boatman', -// }); -// -// assert.strictEqual(res.flagged, true); -// assert.strictEqual(res.categories.violence, true); -// assert.ok('violence' in res.category_scores); -// }); - -// TODO: We need update test to without statement (tested.fineTune.file) -test('Count file tokens', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await files(chat.openai).countFileTokens({ - pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', - purpose: 'fine-tune', - }); - - assert.strictEqual(res, 1889); -}); - -test('Creare fine-tune file', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await files(chat.openai).create({ - pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', - purpose: 'fine-tune', - }); - - assert.ok('id' in res); - assert.strictEqual(res.status, 'processed'); - - tested.fineTune.file.id = res.id; -}); - -test('Create assistant file', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await files(chat.openai).create({ - pathToFile: ASSISTANTS + '/test.csv', - purpose: 'assistants', - }); - - assert.ok('id' in res); - assert.strictEqual(res.status, 'processed'); - - tested.assistant.file.id = res.id; -}); - -test('List files', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await files(chat.openai).list(); - - assert.ok(Array.isArray(res)); -}); - -test('Retrieve file', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const file_id = tested.fineTune.file.id; - const res = await files(chat.openai).retrieve({ file_id }); - - assert.ok('id' in res); - assert.strictEqual(res.id, file_id); -}); - -test('Retrieve file content', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const file_id = tested.fineTune.file.id; - const res = await files(chat.openai).content({ file_id }); - - assert.strictEqual(typeof res, 'string'); -}); - -test('Delete file', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const file_id = tested.fineTune.file.id; - const res = await files(chat.openai).del({ file_id }); - - assert.ok('id' in res); - assert.strictEqual(res.id, file_id); - assert.strictEqual(res.deleted, true); -}); - -test('Create Fine Tune job', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await fineTune(chat.openai).create({ - pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', - }); - - assert.ok('id' in res); - assert.deepEqual(res.error, {}); - - tested.fineTune.id = res.id; - tested.fineTune.file.id = res.training_file; -}); - -test('Create Fine Tune from training file', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const file_id = tested.fineTune.file.id; - const res = await fineTune(chat.openai).create({ - training_file: file_id, - }); - - assert.ok('id' in res); - assert.deepEqual(res.error, {}); - - tested.fineTune.id = res.id; -}); -// // Does not work -// // test('Get Fine Tune events', async () => { -// // // let ftJobId = tested.fineTune.id; -// // const res = await fineTune.events({id:ftJobId}) -// // }); -test('List Fine Tune jobs', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await fineTune(chat.openai).list(); - - assert.ok(Array.isArray(res)); -}); - -test('Retrieve Fine Tune job', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const ftJobId = tested.fineTune.id; - const res = await fineTune(chat.openai).retrieve({ id: ftJobId }); - - assert.ok('id' in res); - assert.strictEqual(res.id, ftJobId); -}); -// Require to catch error if job is completed -// test('Cancel Fine Tune job', async () => { -// // let ftJobId = tested.fineTune.id; -// const fn = async () => await fineTune.cancel({id:ftJobId}); -// expect(fn).toThrow(TypeError); -// }); - -test('List models', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await models(chat.openai).list(); - - assert.ok(Array.isArray(res)); - - const custom_model = res.find((model) => model.id.startsWith('ft')); - tested.model.id = custom_model.id; -}); - -test('Retrieve model', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const res = await models(chat.openai).retrieve({ model_id: modelId }); - - assert.ok('id' in res); - assert.strictEqual(res.id, modelId); -}); - -// WARN: 403 You have insufficient permissions for this operation - -// await t.test('Delete model', async () => { -// const chat = new Chat({ apiKey: API_KEY }); -// -// const res = await models(chat.openai).del({ model_id: modelId }); -// -// assert.ok('deleted' in res); -// assert.ok(res.deleted); -// }); - -test('textToSpeech', async (t) => { - const chat = new Chat({ apiKey: API_KEY }); - - const pathToFile = AUDIOS + '/my_test-speech-output-en.mp3'; - - const isExist = await common.fileIsExist(pathToFile); - - if (isExist) { - try { - await fs.promises.unlink(pathToFile); - } catch (err) { - t.fail(err); - } - } - - const res = await speech(chat.openai).textToSpeech({ - text: 'Hello, how can I help you?', - pathToFile, - }); - const stat = await fs.promises.stat(pathToFile); - - assert.ok(Buffer.isBuffer(res)); - assert.ok('uid' in stat); -}); - -test('speechToText', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const pathToFile = AUDIOS + '/test-speech-input-en.mp3'; - const res = await speech(chat.openai).speechToText({ pathToFile }); - - assert.strictEqual(typeof res, 'string'); -}); - -test('Speech Translation', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const pathToFile = AUDIOS + '/test-speech-input-ru.mp3'; - const res = await speech(chat.openai).speechTranslation({ pathToFile }); - - assert.strictEqual(typeof res, 'string'); -}); - -test('Image Generation', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const saveAs = IMAGES + '/my_test-image-create-result.jpg'; - - const res = await images(chat.openai).create({ - text: 'a white siamese cat', - saveAs, - }); - - assert.ok('url' in res); - assert.ok('local' in res); -}); - -test('Image Edit', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const saveAs = IMAGES + '/my_test-edit-image-result.jpg'; - - const res = await images(chat.openai).edit({ - text: 'A futuristic landscape behind a foregraund emoticon', - pathToFile: IMAGES + '/test-edit-image.png', - pathToMask: IMAGES + '/test-edit-image.png', - saveAs, - size: '256x256', - }); - - assert.ok('url' in res); - assert.ok('local' in res); -}); - -test('Image Variation', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const pathToFile = IMAGES + '/test-edit-image.png'; - const saveAs = IMAGES + '/my_test-image-variation-result.jpg'; - // try { - // await fs.promises.unlink(saveAs) - // } catch (error) {} - - const res = await images(chat.openai).variation({ pathToFile, saveAs }); - // const stat = await fs.promises.stat(saveAs); - - assert.ok('url' in res); - assert.ok('local' in res); - // expect(stat).toHaveProperty('uid'); -}); - -test('Image Recognition', async () => { - const chat = new Chat({ apiKey: API_KEY }); - - const pathToFile = IMAGES + '/test-image.jpg'; - const res = await recognition(chat.openai).image({ pathToFile }); - - assert.ok('message' in res); - assert.ok('usage' in res); -}); -// .....Too heavy for testing -// test('Video Recognition', async () => { -// const pathToFile = './lib/LLM/OpenAI/tests/videos/cat-no.mp4'; -// const outputDir = './lib/LLM/OpenAI/tests/videos/video-frames' -// const res = await recognition.video({ -// pathToFile, -// outputDir, -// frameRate: 5, -// }); -// expect(res).toHaveProperty('message'); -// expect(res).toHaveProperty('usage'); diff --git a/test/openai/chat.js b/test/openai/chat.js new file mode 100644 index 0000000..e4e625b --- /dev/null +++ b/test/openai/chat.js @@ -0,0 +1,40 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); + +const { openai } = require('../../lib'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +const FILES = path.join(__dirname, '../../files'); +const OPEN_AI_FILES = path.join(FILES, 'openai'); +const AUDIOS = path.join(OPEN_AI_FILES, 'audios'); + +describe('Chat Operations', () => { + let chat = null; + beforeEach(() => { + chat = new Chat({ apiKey: API_KEY }); + }); + + it('Chat message', async () => { + const res = await chat.message({ text: 'Hello' }); + + assert.strictEqual(typeof res, 'string'); + }); + + it('Chat voice message', async () => { + const res = await chat.voiceMessage({ + inputFilePath: AUDIOS + '/test-speech-input-en.mp3', + outputFilePath: AUDIOS + '/my_test-speech-output-en.mp3', + returnIntermediateResult: false, + }); + + assert.ok('inputText' in res); + assert.strictEqual(res.inputText, 'Hello there.'); + assert.ok('outputText' in res); + assert.ok('outputFilePath' in res); + }); +}); diff --git a/test/openai/files.js b/test/openai/files.js new file mode 100644 index 0000000..e51fe7d --- /dev/null +++ b/test/openai/files.js @@ -0,0 +1,88 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); + +const { openai } = require('../../lib'); +const utils = require('../../lib/openai/utils'); +const { tested } = require('./tested.js'); + +const FILES = path.join(__dirname, '../../files'); +const OPEN_AI_FILES = path.join(FILES, 'openai'); +const FINE_TUNE = path.join(OPEN_AI_FILES, 'fine-tune'); +const ASSISTANTS = path.join(OPEN_AI_FILES, 'assistants'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +describe('File Operations', () => { + let files; + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + files = utils.files(chat.openai); + }); + + it('Count file tokens', async () => { + const res = await files.countFileTokens({ + pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', + purpose: 'fine-tune', + }); + + assert.strictEqual(res, 1889); + }); + + it('Create fine-tune file', async () => { + const res = await files.create({ + pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', + purpose: 'fine-tune', + }); + + assert.ok('id' in res); + assert.strictEqual(res.status, 'processed'); + + tested.fineTune.file.id = res.id; + }); + + it('Create assistant file', async () => { + const res = await files.create({ + pathToFile: ASSISTANTS + '/test.csv', + purpose: 'assistants', + }); + + assert.ok('id' in res); + assert.strictEqual(res.status, 'processed'); + + tested.assistant.file.id = res.id; + }); + + it('List files', async () => { + const res = await files.list(); + + assert.ok(Array.isArray(res)); + }); + + it('Retrieve file', async () => { + const file_id = tested.fineTune.file.id; + const res = await files.retrieve({ file_id }); + + assert.ok('id' in res); + assert.strictEqual(res.id, file_id); + }); + + it('Retrieve file content', async () => { + const file_id = tested.fineTune.file.id; + const res = await files.content({ file_id }); + + assert.strictEqual(typeof res, 'string'); + }); + + it('Delete file', async () => { + const file_id = tested.fineTune.file.id; + const res = await files.del({ file_id }); + + assert.ok('id' in res); + assert.strictEqual(res.id, file_id); + assert.strictEqual(res.deleted, true); + }); +}); diff --git a/test/openai/finetune.js b/test/openai/finetune.js new file mode 100644 index 0000000..00c8b10 --- /dev/null +++ b/test/openai/finetune.js @@ -0,0 +1,62 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); + +const { openai } = require('../../lib'); +const utils = require('../../lib/openai/utils'); +const { tested } = require('./tested.js'); + +const FILES = path.join(__dirname, '../../files'); +const OPEN_AI_FILES = path.join(FILES, 'openai'); +const FINE_TUNE = path.join(OPEN_AI_FILES, 'fine-tune'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +describe('Fine Tune Operations', () => { + let fineTune = null; + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + fineTune = utils.fineTune(chat.openai); + }); + + it('Create Fine Tune job', async () => { + const res = await fineTune.create({ + pathToFile: FINE_TUNE + '/test-fine-tune-24.jsonl', + }); + + assert.ok('id' in res); + assert.deepEqual(res.error, {}); + + tested.fineTune.id = res.id; + tested.fineTune.file.id = res.training_file; + }); + + it('Create Fine Tune from training file', async () => { + const file_id = tested.fineTune.file.id; + const res = await fineTune.create({ + training_file: file_id, + }); + + assert.ok('id' in res); + assert.deepEqual(res.error, {}); + + tested.fineTune.id = res.id; + }); + + it('List Fine Tune jobs', async () => { + const res = await fineTune.list(); + + assert.ok(Array.isArray(res)); + }); + + it('Retrieve Fine Tune job', async () => { + const ftJobId = tested.fineTune.id; + const res = await fineTune.retrieve({ id: ftJobId }); + + assert.ok('id' in res); + assert.strictEqual(res.id, ftJobId); + }); +}); diff --git a/test/openai/images.js b/test/openai/images.js new file mode 100644 index 0000000..339a1e4 --- /dev/null +++ b/test/openai/images.js @@ -0,0 +1,60 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); + +const { openai } = require('../../lib'); +const utils = require('../../lib/openai/utils'); + +const FILES = path.join(__dirname, '../../files'); +const OPEN_AI_FILES = path.join(FILES, 'openai'); +const IMAGES = path.join(OPEN_AI_FILES, 'images'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +describe('Image Operations', () => { + let images = null; + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + images = utils.images(chat.openai); + }); + + it('Image Generation', async () => { + const saveAs = IMAGES + '/my_test-image-create-result.jpg'; + + const res = await images.create({ + text: 'a white siamese cat', + saveAs, + }); + + assert.ok('url' in res); + assert.ok('local' in res); + }); + + it('Image Edit', async () => { + const saveAs = IMAGES + '/my_test-edit-image-result.jpg'; + + const res = await images.edit({ + text: 'A futuristic landscape behind a foregraund emoticon', + pathToFile: IMAGES + '/test-edit-image.png', + pathToMask: IMAGES + '/test-edit-image.png', + saveAs, + size: '256x256', + }); + + assert.ok('url' in res); + assert.ok('local' in res); + }); + + it('Image Variation', async () => { + const pathToFile = IMAGES + '/test-edit-image.png'; + const saveAs = IMAGES + '/my_test-image-variation-result.jpg'; + + const res = await images.variation({ pathToFile, saveAs }); + + assert.ok('url' in res); + assert.ok('local' in res); + }); +}); diff --git a/test/openai/language.js b/test/openai/language.js new file mode 100644 index 0000000..870770e --- /dev/null +++ b/test/openai/language.js @@ -0,0 +1,61 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); + +const { openai } = require('../../lib'); +const utils = require('../../lib/openai/utils'); + +const FILES = path.join(__dirname, '../../files'); +const OPEN_AI_FILES = path.join(FILES, 'openai'); +const TOOLS = path.join(OPEN_AI_FILES, 'tools'); +const TEST_LIBRARY = require(TOOLS + '/test-library.js'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +describe('Chat Operations', () => { + let language = null; + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + language = utils.language(chat.openai); + }); + + it('Text completion', async () => { + const res = await language.generate({ text: 'hello' }); + + assert.ok('message' in res); + assert.ok('messages' in res); + assert.ok('usage' in res); + }); + + it('Text completion with function call', async () => { + const res = await language.generate({ + text: 'What is the weather like in San Francisco, Tokyo and Paris?', + tools: TEST_LIBRARY.tools, + }); + + assert.ok('message' in res); + assert.ok('messages' in res); + assert.ok('usage' in res); + }); + + it('Text Embedding', async () => { + const res = await language.embedding({ text: 'hello' }); + + assert.ok('embedding' in res); + assert.ok('usage' in res); + }); + + // WARN: 400 Invalid value for 'model' = text-moderation-007 + it.skip('Text Classification', async () => { + const res = await language.classification({ + text: 'I will kill you boatman', + }); + + assert.strictEqual(res.flagged, true); + assert.strictEqual(res.categories.violence, true); + assert.ok('violence' in res.category_scores); + }); +}); diff --git a/test/openai/models.js b/test/openai/models.js new file mode 100644 index 0000000..4bd68f7 --- /dev/null +++ b/test/openai/models.js @@ -0,0 +1,45 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); + +const { openai } = require('../../lib'); +const utils = require('../../lib/openai/utils'); +const { tested } = require('./tested.js'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +const modelId = 'gpt-3.5-turbo-16k'; + +describe('Model Operations', () => { + let models = null; + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + models = utils.models(chat.openai); + }); + + it('List models', async () => { + const res = await models.list(); + + assert.ok(Array.isArray(res)); + + const custom_model = res.find((model) => model.id.startsWith('ft')); + tested.model.id = custom_model.id; + }); + + it('Retrieve model', async () => { + const res = await models.retrieve({ model_id: modelId }); + + assert.ok('id' in res); + assert.strictEqual(res.id, modelId); + }); + + // WARN: 403 You have insufficient permissions for this operation + it.skip('Delete model', async () => { + const res = await models.del({ model_id: modelId }); + + assert.ok('deleted' in res); + assert.ok(res.deleted); + }); +}); diff --git a/test/openai/recognition.js b/test/openai/recognition.js new file mode 100644 index 0000000..7bebabc --- /dev/null +++ b/test/openai/recognition.js @@ -0,0 +1,31 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); + +const { openai } = require('../../lib'); +const utils = require('../../lib/openai/utils'); + +const FILES = path.join(__dirname, '../../files'); +const OPEN_AI_FILES = path.join(FILES, 'openai'); +const IMAGES = path.join(OPEN_AI_FILES, 'images'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +describe('Recognition Operations', () => { + let recognition = null; + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + recognition = utils.recognition(chat.openai); + }); + + it('Image Recognition', async () => { + const pathToFile = IMAGES + '/test-image.jpg'; + const res = await recognition.image({ pathToFile }); + + assert.ok('message' in res); + assert.ok('usage' in res); + }); +}); diff --git a/test/openai/speech.js b/test/openai/speech.js new file mode 100644 index 0000000..90da3ba --- /dev/null +++ b/test/openai/speech.js @@ -0,0 +1,62 @@ +'use strict'; + +const { beforeEach, it, describe } = require('node:test'); +const assert = require('node:assert'); +const path = require('node:path'); +const fs = require('node:fs/promises'); + +const { openai } = require('../../lib'); +const utils = require('../../lib/openai/utils'); +const common = require('../../lib/common.js'); + +const FILES = path.join(__dirname, '../../files'); +const OPEN_AI_FILES = path.join(FILES, 'openai'); +const AUDIOS = path.join(OPEN_AI_FILES, 'audios'); + +const { Chat } = openai; +const API_KEY = process.env.OPENAI_API_KEY; + +describe('Speech Operations', () => { + let speech = null; + beforeEach(() => { + const chat = new Chat({ apiKey: API_KEY }); + speech = utils.speech(chat.openai); + }); + + it('textToSpeech', async () => { + const pathToFile = AUDIOS + '/my_test-speech-output-en.mp3'; + + const isExist = await common.fileIsExist(pathToFile); + + if (isExist) { + try { + await fs.unlink(pathToFile); + } catch (err) { + assert.fail(err); + } + } + + const res = await speech.textToSpeech({ + text: 'Hello, how can I help you?', + pathToFile, + }); + const stat = await fs.stat(pathToFile); + + assert.ok(Buffer.isBuffer(res)); + assert.ok('uid' in stat); + }); + + it('speechToText', async () => { + const pathToFile = AUDIOS + '/test-speech-input-en.mp3'; + const res = await speech.speechToText({ pathToFile }); + + assert.strictEqual(typeof res, 'string'); + }); + + it('Speech Translation', async () => { + const pathToFile = AUDIOS + '/test-speech-input-ru.mp3'; + const res = await speech.speechTranslation({ pathToFile }); + + assert.strictEqual(typeof res, 'string'); + }); +}); diff --git a/test/openai/tested.js b/test/openai/tested.js new file mode 100644 index 0000000..9b483d9 --- /dev/null +++ b/test/openai/tested.js @@ -0,0 +1,28 @@ +'use strict'; + +const { openai } = require('../../lib'); +const { Chat } = openai; + +const tested = { + chat: new Chat({}), + fineTune: { + id: '', + file: { id: '' }, + }, + model: { + id: '', + }, + assistant: { + id: '', + thread: { id: '' }, + message: { id: '' }, + run: { id: '' }, + step: { id: '' }, + file: { id: '' }, + added_file: { id: '' }, + }, +}; + +module.exports = { + tested, +};